iSO底层原理 - Block的本质

160 阅读1分钟

Block的本质:

  • block本质上也是一个OC对象,它内部也有个isa指针;
  • block是封装了函数调用以及函数调用环境的OC对象;

Block的数据结构


block的变量捕获(capture)

  • 为了保证block内部能够正常访问外部的变量,block有个变量捕获机制;

  • 1.局部变量需要捕获;

  • 2.全局变量不需要捕获,直接访问就可以。

局部变量

        // auto:自动变量,离开作用域就销毁
        //默认就是auto
        auto int age = 10; 
        static int height = 10;
        void (^block)(void) = ^{
            // age的值捕获进来(capture)
            NSLog(@"age is %d, height is %d", age, height);
        };
        age = 20;
        height = 20;
        block();
  • 打印结果是:age = 10; height = 20。
  • 底层结构如下:
struct __test_block_impl_0 {
  struct __block_impl impl;
  struct __test_block_desc_0* Desc;
  int age;
  int *height;
  __test_block_impl_0(void *fp, struct __test_block_desc_0 *desc, int _age, int *_height, int flags=0) : age(_age), height(_height) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

1> 创建block的时候,直接 auto修饰 的age封装到了block中了,block外再修改,不会影响block内部的age;
2> static修饰的height,传到block内部的是一个指针;外部修改,也会影响block内部的结果。

为何这样设置 ?

  • 因为 auto修饰的局部变量,离开作用域后就会销毁;如果也采用指针访问,那此时访问到的就是垃圾数据。

扩展 :self 会被捕获吗 ?

- (void)test {
    void (^block)(void) = ^{
        NSLog(@"-------%p", self);
    };
    block();
}
  • 会;
  • self其实是一个参数,参数是一个局部变量。
// test函数完整写法如下:
- (void)test(Object *self, SEL _cmd) {
}