iOS ---block

2020-09-15  本文已影响0人  iOS程序媛ing

block是封装了函数调用和函数调用环境的oc对象

(一)block的三种形式

block有三种形式,全局block、栈block、堆block

block类型 调用环境
NSGlobalBlock 没有访问auto变量
NSStackBlock 访问auto变量
NSMallocBlock stackBlock调用copy

每种类型block调用copy的结果

block类型 执行copy操作
NSGlobalBlock 没有变化
NSStackBlock 从栈区拷贝到兑取
NSMallocBlock 引用计数增加

(1)全局block,不使用外部变量的是全局block

NSString *str = @"123";
    void (^block)(void) = ^{
        
    };
    block();
    NSLog(@"=======%@", block);

打印结果

2020-09-13 16:59:41.768259+0800 面试[4914:234355] =======<__NSGlobalBlock__: 0x106f0f840>

(2)堆block,使用外部变量的是堆block

NSString *str = @"123";
    void (^block)(void) = ^{
        NSLog(@"%@", str);
    };
    block();
    NSLog(@"=======%@", block);

打印结果

2020-09-13 17:00:42.136964+0800 面试[4930:235455] 123
2020-09-13 17:00:42.137254+0800 面试[4930:235455] =======<__NSMallocBlock__: 0x600001600090>

(二)block内部使用变量

(1)局部变量---block对局部变量是值捕获
①先修改count的值,再调用block,打印结果仍为未修改的值,可以说明block对局部变量是值捕获

NSInteger count = 123;
    void (^block)(void) = ^{

        NSLog(@"count=%ld", count);
    };
    count = 456;
    block();

打印结果为

2020-09-14 11:26:51.929949+0800 面试[5623:279738] count=123
NSInteger count = 123;
    void (^block)(void) = ^{
        count = 456;
    };
    block();

(2)静态局部变量---block对静态局部变量是指针捕获

static NSInteger count = 123;
    void (^block)(void) = ^{

        NSLog(@"count=%ld", count);
    };
    count = 456;
    block();

打印结果

2020-09-14 15:10:54.928932+0800 面试[6095:316810] count=456
static NSInteger count = 123;
    void (^block)(void) = ^{
        count = 789;
        NSLog(@"count=%ld", count);
    };
    count = 456;
    block();

打印结果

2020-09-14 15:12:53.073080+0800 面试[6194:319075] count=789

(3)全局变量和静态全局变量-----block不需要捕获,可以直接使用

int a = 10;
static int b = 20;
static NSInteger count = 123;
    void (^block)(void) = ^{
        a = 1000;
        b = 2000;
        NSLog(@"a=%d  b= %d", a, b);
    };
    a = 100;
    b = 200;
    block();

打印结果

2020-09-14 15:14:48.444173+0800 面试[6241:321353] a=100  b= 200

   static NSInteger count = 123;
    void (^block)(void) = ^{

        NSLog(@"a=%d  b= %d", a, b);
    };
    a = 100;
    b = 200;
    block();

打印结果

2020-09-14 15:16:06.276051+0800 面试[6262:322567] a=100  b= 200

(三)__block的使用

前面我们说的第一种情况,block使用局部变量,block内部不可以修改局部变量的值,但如果局部变量使用__block修饰,block内部就可以修改局部变量

   __block NSInteger count = 123;
       void (^block)(void) = ^{
           count = 789;
           NSLog(@"count=%ld", count);
       };
       count = 456;
       block();

打印结果

2020-09-14 11:29:37.590054+0800 面试[5639:281495] count=789

这是因为用__block修饰的变量count,被block包装成一个结构体,结构体包含count,block内部获取了指向结构体的指针,所以当count改变时,block通过结构体指针找到结构体,继而找到count,因为结构体里的count和外界的count指向的是同一地址。

__block NSInteger count = 123;
       void (^block)(void) = ^{
           count = 789;
           NSLog(@"count=%ld", count);
       };
       count = 456;
       block();
    NSLog(@"======%ld", (long)count);

打印结果为

2020-09-14 15:49:13.601098+0800 面试[6554:343470] count=789
2020-09-14 15:49:13.601287+0800 面试[6554:343470] ======789

   __block NSInteger count = 123;
       void (^block)(void) = ^{
           NSLog(@"count=%ld", count);
       };
       count = 456;
       block();
    NSLog(@"======%ld", (long)count);

打印结果

2020-09-14 15:50:46.372492+0800 面试[6579:344931] count=456
2020-09-14 15:50:46.372668+0800 面试[6579:344931] ======456

(四)block为什么用copy修饰

因为使用block内部使用了变量的block是栈block,栈区由系统分配和回收内存,可能我们还需要使用block的时候,block已经被系统回收了,所以为了避免这种情况,我们需要将block从栈区拷贝到堆区,由程序员来管理block。

系统默认情况下会将block从栈区拷贝到堆区的几种情况
[UIView animateWithDuration:1 animations:^{
        
    }];

 [[NSNotificationCenter defaultCenter] addObserverForName:@"1" object:@"" queue:[NSOperationQueue currentQueue] usingBlock:^(NSNotification * _Nonnull note) {
        
    }];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
        
    }];
dispatch_async(dispatch_get_main_queue(), ^{
        
    });

但是如果,这些方法中,使用了成员变量,block中,再调用self,就会造成循环饮用。如

    __weak  __typeof__(self)    weakSelf    =   self;   
    _observer   =   [[NSNotificationCenter defaultCenter]   addObserverForName:@"testKey"
object:nil
queue:nil
usingBlock:^(NSNotification *note)  {   
__typeof__(self)    strongSelf  =   weakSelf;   
                    [strongSelf dismissModalViewControllerAnimated:YES];    
    }];


dispatch_group_async(_operationsGroup,  _operationsQueue,   ^   
{   
__typeof__(self)    strongSelf  =   weakSelf;   
[strongSelf doSomething];   
[strongSelf doSomethingElse];   
}   );

(五)block能否改变NSArray

①编译时报错---不能

   NSMutableArray *array = [NSMutableArray arrayWithObjects:@"1",@"2", nil];
       void (^block)(void) = ^{
           array = [NSMutableArray array];
       };
 
       block();

②可以修改array的属性

   NSMutableArray *array = [NSMutableArray arrayWithObjects:@"1",@"2", nil];
       void (^block)(void) = ^{
           [array addObject:@"3"];
           NSLog(@"%@", array);
       };
    [array addObject:@"4"];
       block();

打印结果

2020-09-14 15:56:10.471593+0800 面试[6617:348591] (
    1,
    2,
    4,
    3
)

block引起的循环引用问题

最近在做类似钉钉打卡的功能,需要检测网络状态,改变UI显示样式,在没有网络的情况下,要显示不能打卡。在有网的情况下,正常显示,
我是用的是AFNetworkReachabilityManager

如下。我在block中使用了self,而不是weakSelf,导致了循环引用,控制器不能释放问题。

- (void)monitorReachabilityStatus {
    // 开始监测
    [[AFNetworkReachabilityManager sharedManager] startMonitoring];
    // 网络状态改变的回调
    MJWeakSelf;
    [[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
        
        switch (status) {
            case AFNetworkReachabilityStatusReachableViaWWAN:
                NSLog(@"蜂窝网络");
                self.view.backgroundColor = [UIColor redColor];
                break;
            case AFNetworkReachabilityStatusReachableViaWiFi:
                NSLog(@"WIFI");
                self.view.backgroundColor = [UIColor redColor];
                break;
            case AFNetworkReachabilityStatusNotReachable:
                NSLog(@"没有网络");
                self.view.backgroundColor = [UIColor redColor];
                break;
            case AFNetworkReachabilityStatusUnknown:
                NSLog(@"未知");
                self.view.backgroundColor = [UIColor redColor];
                break;
            default:
                break;
        }

    }];
}

解决方案:只需将self改为weakself即可

- (void)monitorReachabilityStatus {
    // 开始监测
    [[AFNetworkReachabilityManager sharedManager] startMonitoring];
    // 网络状态改变的回调
    MJWeakSelf;
    [[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
        
        switch (status) {
            case AFNetworkReachabilityStatusReachableViaWWAN:
                NSLog(@"蜂窝网络");
                weakSelf.view.backgroundColor = [UIColor redColor];
                break;
            case AFNetworkReachabilityStatusReachableViaWiFi:
                NSLog(@"WIFI");
                weakSelf.view.backgroundColor = [UIColor redColor];
                break;
            case AFNetworkReachabilityStatusNotReachable:
                NSLog(@"没有网络");
                weakSelf.view.backgroundColor = [UIColor redColor];
                break;
            case AFNetworkReachabilityStatusUnknown:
                NSLog(@"未知");
                weakSelf.view.backgroundColor = [UIColor redColor];
                break;
            default:
                break;
        }

    }];
}
}

上一篇 下一篇

猜你喜欢

热点阅读