美文网首页iOS底层基础知识iOS面试知识点iOS 的那些事儿
iOS-底层原理(8)-block-本质,类型,copy属性详解

iOS-底层原理(8)-block-本质,类型,copy属性详解

作者: 路飞_Luck | 来源:发表于2018-09-01 15:58 被阅读102次

    1.Block的本质

    • block本质上也是一个OC对象,它内部也有个isa指针
    • block是封装了函数调用以及函数调用环境的OC对象
    • block的底层结构如下图所示
    image.png image.png

    代码佐证

    struct __main_block_desc_0 {
        size_t reserved;
        size_t Block_size;
    };
    
    struct __block_impl {
        void *isa;
        int Flags;
        int Reserved;
        void *FuncPtr;
    };
    
    // block底层结构体
    struct __main_block_impl_0 {
        struct __block_impl impl;
        struct __main_block_desc_0* Desc;
        int age;
    };
    
    // 1.block结构体
    void test1() {
        int age = 10;
        
        // 定义block
        void(^block)(int, int) = ^(int a, int b){
            NSLog(@"this is a block! -- %d", age);
            NSLog(@"a = %d, b = %d",a,b);
        };
        
        struct __main_block_impl_0 *blockStruct = (__bridge struct __main_block_impl_0 *)block;
        block(100,200);
    }
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            // insert code here...
            
            // 1. block结构体
            test1();
        }
        return 0;
    }
    
    image.png
    2.block的变量捕获(capture)
    • 为了保证block内部能够正常访问外部的变量,block有个变量捕获机制
    image.png
    • auto:自动变量,离开作用域就销毁

    代码例子如下:

    // 捕获变量类型
    void (^block)(void);
    
    void blockTest() {
        int age = 10;
        static int height = 10;
        
        block = ^ {
            NSLog(@"age is %d, height is %d", age, height);
        };
        
        age = 20;
        height = 20;
    }
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            blockTest();
            block();
        }
        return 0;
    }
    

    运行结果

    image.png
    3.block的类型
    • block有3种类型,可以通过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型
    • __NSGlobalBlock__ (_NSConcreteGlobalBlock )
    • __NSStackBlock__ ( _NSConcreteStackBlock )
    • __NSMallocBlock__ ( _NSConcreteMallocBlock )
    image.png image.png
    • 每一种类型的block调用copy后的结果如下所示
    image.png

    代码例子如下

    • 先将环境切换为MRC,setting -> OC Automitic RF -> NO
    // 4.block的类型
    int weight = 100;
    void (^block)(void);
    
    void blockClassType() {
        // 堆:动态分配内存,需要程序员申请申请,也需要程序员自己管理内存
        static int age = 10;
        // 局部static变量
        int height = 10;
        
        // Global:没有访问auto变量
        void(^block1)(void) = ^ {
            NSLog(@"block1");
        };
        // Globa2:访问static变量
        void(^block2)(void) = ^ {
            NSLog(@"block2 - age = %d",age);
        };
        // Globa3:访问全局变量
        void(^block3)(void) = ^ {
            NSLog(@"block3 - weight = %d",weight);
        };
        
        // Stack:访问了auto变量
        void (^block4)(void) = ^{
            NSLog(@"block4 - height = %d", height);
        };
        
        // NSMallocBlock - 对StackBlock做copy操作
        block = [^{
            NSLog(@"block---------%d", height);
        } copy];
        [block release];
        
        NSLog(@"%@ %@ %@ %@ %@",
              [block1 class],
              [block2 class],
              [block3 class],
              [block4 class],
              [block class]
        );
    }
    

    运行结果如下

    image.png
    4. 数据存储位置

    代码例子如下

    // 5.数据存储位置
    int age = 100;
    void dataLocationTest() {
        int a = 10;
        
        NSLog(@"数据段:age %p", &age);
        NSLog(@"栈:a %p", &a);
        NSLog(@"堆:obj %p", [[NSObject alloc] init]);
        NSLog(@"数据段:class %p", [Person class]);
    }
    

    打印结果

    image.png
    5.block的copy

    在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上,比如以下情况

    • block作为函数返回值时
    • 将block赋值给__strong指针时
    • block作为Cocoa API中方法名含有usingBlock的方法参数时
    • block作为GCD API的方法参数时

    代码例子如下

    • 1.block作为函数返回值时
    // 定义一个block
    typedef void(^CSBlock)(void);
    
    // 定义一个返回blcok的函数
    CSBlock myBlock() {
        int age = 10;
        return ^{
            NSLog(@"age = %d",age);
        };
    }
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            // insert code here...
            
            // 1.block作为函数返回值
            CSBlock block = myBlock();
            block();
            NSLog(@"%@",[block class]);
        }
        return 0;
    }
    

    运行结果

    image.png
    • 2.将block赋值给__strong指针时
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            // 2.block有被强指针引用
            // ARC - 环境下 block - __NSMallocBlock__
            // MRC - 环境下 block - __NSStackBlock__
            int age = 10;
            CSBlock block = ^{
                NSLog(@"---------%d", age);
            };
            NSLog(@"%@", [block class]);
        }
        return 0;
    }
    

    打印结果

    ARC.png MRC.png
    • 3.block作为Cocoa API中方法名含有usingBlock的方法参数时
    // 3.block作为Cocoa API中方法名含有usingBlock的方法参数时
    NSArray *array = [NSArray array];
    [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
                
    }];
    
    • 4.block作为GCD API的方法参数时
    // 4.block作为GCD API的方法参数时
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{            
    });
    
    block建议写法
    • MRC下block属性的建议写法
      @property (copy, nonatomic) void (^block)(void);

    • ARC下block属性的建议写法
      @property (strong, nonatomic) void (^block)(void);
      @property (copy, nonatomic) void (^block)(void);


    本文参考借鉴MJ的教程视频,非常感谢.


    项目演示代码如下
    iOS_block_本质
    iOS-block-copy


    更多block相关文章

    iOS-copy底层原理之auto变量

    iOS-block底层原理详解之__block属性

    iOS-block底层原理之循环引用详解

    相关文章

      网友评论

        本文标题:iOS-底层原理(8)-block-本质,类型,copy属性详解

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