美文网首页
Block 之 block的类型

Block 之 block的类型

作者: ychen3022 | 来源:发表于2018-10-30 00:45 被阅读0次
    前言:追溯block的父类,看清其本质
    void(^block)(void) = ^{
            NSLog(@"Hello world");
        };
    
        NSLog(@"%@",[block class]);
        NSLog(@"%@",[[block class] superclass]);
        NSLog(@"%@",[[[block class] superclass] superclass]);
        NSLog(@"%@",[[[[block class] superclass] superclass] superclass]);
    
    ====================================================
    打印结果:BlockTest[2295:101750] __NSGlobalBlock__
             BlockTest[2295:101750] __NSGlobalBlock
             BlockTest[2295:101750] NSBlock
             BlockTest[2295:101750] NSObject
    

    前面我们也说过,block本质上就是一个OC对象。
    从上面的代码打印结果,我们更可以看出,block 就是一个对象,而且追溯其父类,最终可以发现是个NSObject对象。
    他有isa指针,从NSObject而来。

    1、block的类型

    下面我们通过一段代码来证实block的类型:
    注意:要将XCode设为MRC,因为ARC下,编译器会对block进行copy,做些保护操作。

    屏幕快照 2018-10-29 下午5.39.13.png

    运行下面的代码,我们能够看到block有三种类型。

    - (void)viewDidLoad {
        [super viewDidLoad];
    
        void(^block1)(void) = ^{
            NSLog(@"Hello world");
        };
    
        int age = 10;
        void(^block2)(void) = ^{
            NSLog(@"age is %d",age);
        };
    
        void(^block3)(void) = [^{
            NSLog(@"age is %d",age);
        } copy];
    
        NSLog(@"第一种:%@",[block1 class]);
        NSLog(@"第二种:%@",[block2 class]);
        NSLog(@"第三种:%@",[block3 class]);
    }
    
    =========================================
    打印结果:
    BlockTest[2814:131802] 第一种:__NSGlobalBlock__
    BlockTest[2814:131802] 第二种:__NSStackBlock__
    BlockTest[2814:131802] 第三种:__NSMallocBlock__
    

    结论:block有3种类型,可以通过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型。

    屏幕快照 2018-10-29 下午3.02.32.png
    • 那这三种block类型是依据什么来确定类型的呢?看下图
      image.png

    1.对于NSGlobalBlock,我们不用太过操心。
    2.但是NSStackBlock在栈上存储,而block经常在别处调用,后续调用的时候并不能保证定义的那个block还在,这种类型的block有风险。但NSStackBlock调用copy操作后就会变成NSMallocBlock类型,存放到堆上了,便没有这个风险了。
    (从上面代码block2->block3也可以证实)
    3、NSMallocBlock类型的block由程序员手动管理,内存安全。

    • 调用copy后,NSStackBlock转为NSMallocBlock,那其他两种block类型调用copy会怎么样呢?看下图总结
      image.png
    2、ARC下Block的自动copy操作

    在上面,我们已经讲过block的copy操作了(基于MRC,ARC情况下编译器会自动帮我们做很多的操作来保护block不被释放)。
    那我们再来详细分析一下。
    在MRC环境下:

    //在MRC环境下运行这段代码
    typedef void(^MyBlock)();
        int age = 10;
         //“=”这个地方是有强指针指向block
        MyBlock block = ^{
            NSLog(@"this is a block,age = %d",age);
        };
        block();
        
        NSLog(@"block的类型 %@",[block class]);
        NSLog(@"block copy之后的类型 %@",[[block copy] class]);
    
    ==================================================
    打印结果:
    BlockTest[6676:445306] this is a block,age = 10
    BlockTest[6676:445306] block的类型 __NSStackBlock__
    BlockTest[6676:445306] block copy之后的类型 __NSMallocBlock__
    

    在ARC环境下:

    //在ARC环境下运行这段代码
    typedef void(^MyBlock)();
        int age = 10;
        // “=”这个地方是有强指针指向block
        MyBlock block = ^{
            NSLog(@"this is a block,age = %d",age);
        };
        block();
        
        NSLog(@"block的类型 %@",[block class]);
        NSLog(@"block copy之后的类型 %@",[[block copy] class]);
    
    ==================================================
    打印结果:
    BlockTest[6735:449326] this is a block,age = 10
    BlockTest[6735:449326] block的类型 __NSMallocBlock__
    BlockTest[6735:449326] block copy之后的类型 __NSMallocBlock__
    

    从上面可以看出,在ARC环境下编译器会根据情况自动将栈上的block复制到堆上。那什么情况会让ARC给block加copy操作呢?

    • 1、block作为函数返回值时
    • 2、将block赋值给__strong指针时
    • 3、block作为Cocoa API中方法名含有usingBlock的方法参数时
    • 4、block作为GCD API的方法参数时
        //情况一:block作为函数返回值时
        typedef void(^MjBlock)();
        MjBlock mjBlock()
        {
            int age = 10;
            return ^{
                NSLog(@"age = %d",age);
            };
        }
        
        
        //情况二:将block赋值给__strong指针时
        typedef void(^MyBlock)();
        int age = 10;
        //这种被强指针指向,会自动copy,打印结果为:block的类型 __NSMallocBlock__
        MyBlock block = ^{
            NSLog(@"this is a block,age = %d",age);
        };
        NSLog(@"block的类型 %@",[block class]);
        
        //这种没有被强指针指向,就不会自动copy,打印结果为:block的类型 __NSStackBlock__
        NSLog(@"block的类型 %@",[^{
            NSLog(@"this is a block,age = %d",age);
        } class]);
        
        
        //情况三:block作为Cocoa API中方法名含有usingBlock的方法参数时
        NSArray *arrar = @[@"1",@"2",@"3"];
        //系统api,block作为参数
        [arrar enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            
        }];
        
        
        //情况四:block作为GCD API的方法参数时
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            
        });
        
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            
        });
    

    所以在ARC下,我们可以省好多事情,系统已经帮我们考虑好了,我们在写block的时候,按照规定写就好。

    //MRC下block属性的建议写法
    @property (copy, nonatomic) void (^block)(void);
    
    //ARC下block属性的建议写法
    @property (strong, nonatomic) void (^block)(void);
    @property (copy, nonatomic) void (^block)(void);
    
    3、总结
    • <1>block有哪些类型?
      三种类型,
      NSGlobalBlock_:没有引用外部auto变量的、
      NSStackBlock:引用了外部auto变量的、
      NSMallocBlockNSStackBlock类型block调用copy后的。

    • <2>三种类型block调用copy操作后会怎么样?
      NSGlobalBlock_:不变,依然存在数据区、
      NSStackBlock:从栈区复制到堆区、
      NSMallocBlock:还在堆区,引用计数器增加。

    相关文章

      网友评论

          本文标题:Block 之 block的类型

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