【iOS】Block底层(一)

201 阅读4分钟

底层结构

void (^block)(void);

void test()
{
    int age = 10;

    block = ^{
        // age的值捕获进来(capture)
        NSLog(@"age is %d", age);
    };
    age = 20;
    block();
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        test();
    }
    return 0;
}

命令行输入objective-c转c++

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m

查看关于block的定义

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  // 生成age成员变量
  int age;
  // 构造函数(类似于OC的init方法),返回结构体对象
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

struct __block_impl {
    void *isa;
    int Flags;
    int Reserved;
    void *FuncPtr;
};

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
}

可以看出

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

Block定义


自动变量

void (^block)(void);

void test()
{
    int age = 10;

    block = ^{
        // age的值捕获进来(capture)
        NSLog(@"age is %d", age);
    };
    age = 20;
    block();
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        test();
    }
    return 0;
}

打印结果

10

查看源码,可以看到__test_block_impl_0有个成员变量age, 这里的age(_age)相当于把_age值赋值给age.所以当定义block时候,已经将age值赋值给了block内部的成员变量,当执行age = 20;只是赋值给外面的age,执行block(),打印出来的值还是捕获到的age值为10.

// test
struct __test_block_impl_0 {
  struct __block_impl impl;
  struct __test_block_desc_0* Desc;
  int age;
  __test_block_impl_0(void *fp, struct __test_block_desc_0 *desc, int _age, int *_height, int flags=0) : age(_age) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

// 封装了block执行逻辑的函数
static void __test_block_func_0(struct __test_block_impl_0 *__cself) {
  int age = __cself->age; // bound by copy
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_d2875b_mi_0, age);
    }
    

static 变量

void test()
{
    int age = 10;
    static int height = 10;
    
    block = ^{
        // age的值捕获进来(capture)
        NSLog(@"age is %d, height is %d", age, height);
    };
    
    age = 20;
    height = 20;
}

打印结果

age is 10, height 20

height为什么是20呢。查看c++源码

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;
  }
};
static void __test_block_func_0(struct __test_block_impl_0 *__cself) {
  int age = __cself->age; // bound by copy
  int *height = __cself->height; // bound by copy


        NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_d2875b_mi_0, age, (*height));
    }

发现block捕获的*height,也就是指针所指向的地址值,所以height打印值为20

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        test();
        block();
}

从生命周期的角度来说,当代码执行到block的时候,age的已经被销毁,所以不可能去捕获age的内存地址值,而static形容的变量则相反


全局变量

int age_ = 10;
static int height_ = 10;
void (^block)(void);

void test()
{
    block = ^{
        NSLog(@"age is %d, height is %d", age_, height_);
    };
    age_ = 20;
    height_ = 30;
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        test();
        block();
        return 0;
    }
}

打印结果

2020-06-29 11:56:51.415416+0800 TestBlock[2947:10774120] age is 20, height is 30

查看c++源码,可以发现并未捕获全局变量

struct __test_block_impl_0 {
  struct __block_impl impl;
  struct __test_block_desc_0* Desc;
  __test_block_impl_0(void *fp, struct __test_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __test_block_func_0(struct __test_block_impl_0 *__cself) {

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_zz_v5rdxc250h53xq3b41vnsd3c0000gn_T_main_231ff0_mi_0, age_, height_);
    }

关于self

观察self是否会被block捕获?

@interface RLPerson : NSObject
@property (copy, nonatomic) NSString *name;

- (void)test;

- (instancetype)initWithName:(NSString *)name;

@end

@impletion RLPerson
int age_ = 10;

- (void)test
{
    void (^block)(void) = ^{
        NSLog(@"-------%@", self);
        NSLog(@"-------%@", self.name);
        NSLog(@"-------%@", [self name]);
    };
    block();
}

- (instancetype)initWithName:(NSString *)name
{
    if (self = [super init]) {
        self.name = name;
    }
    return self;
}
@end 

查看c++源码,发现self被捕获,而且当调用self.name, [self name],捕获的依然是self

struct __MJPerson__test_block_impl_0 {
  struct __block_impl impl;
  struct __MJPerson__test_block_desc_0* Desc;
  MJPerson *self;
  __MJPerson__test_block_impl_0(void *fp, struct __MJPerson__test_block_desc_0 *desc, MJPerson *_self, int flags=0) : self(_self) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

static void __MJPerson__test_block_func_0(struct __MJPerson__test_block_impl_0 *__cself) {
  MJPerson *self = __cself->self; // bound by copy

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_zz_v5rdxc250h53xq3b41vnsd3c0000gn_T_MJPerson_35d51b_mi_0, self);
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_zz_v5rdxc250h53xq3b41vnsd3c0000gn_T_MJPerson_35d51b_mi_1, ((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("name")));
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_zz_v5rdxc250h53xq3b41vnsd3c0000gn_T_MJPerson_35d51b_mi_2, ((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("name")));
    }

观察- (void)test和- (instancetype)initWithName:(NSString *)name方法源码,会发现self是作为参数传递进来,所以他是局部变量,局部变量是会被block捕获的.**其实方法中都会带有俩个隐式参数 self和 _cmd

static void _I_MJPerson_test(RLPerson * self, SEL _cmd) {
    void (*block)(void) = ((void (*)())&__MJPerson__test_block_impl_0((void *)__MJPerson__test_block_func_0, &__MJPerson__test_block_desc_0_DATA, self, 570425344));
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}


static instancetype _I_MJPerson_initWithName_(RLPerson * self, SEL _cmd, NSString *name) {
    if (self = ((MJPerson *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("MJPerson"))}, sel_registerName("init"))) {
        ((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)self, sel_registerName("setName:"), (NSString *)name);
    }
    return self;
}

总结

  • Block底层结构

    image

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

    捕获机制