美文网首页
iOS ---block

iOS ---block

作者: iOS程序媛ing | 来源:发表于2020-09-15 10:03 被阅读0次

    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从栈区拷贝到堆区的几种情况
    • block作为函数返回值
    • block为GCD的参数
    • block被强引用的情况下
    • block作为Cocoa API 方法名中包含using block的方法参数时
      比如以下,在block中调用使用self,都不会造成循环饮用:
    [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;
            }
    
        }];
    }
    }
    
    

    相关文章

      网友评论

          本文标题:iOS ---block

          本文链接:https://www.haomeiwen.com/subject/eztoektx.html