The underlying structure

void (^block)(void);

void test() { int age = 10; Block = ^{// the value of age is captured in NSLog(@)"age is %d", age);
    };
    age = 20;
    block();
}

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

Enter Objective-C to c++

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

See the definition of blocks

struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; // Generate age member variable int age; // Constructor (similar to OC init method), Struct __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; }Copy the code

It can be seen that

  • The block has an ISA pointer inside it, so it is also essentially an OC object
  • A block is an OC object that encapsulates a function call and its environment


Automatic variables

void (^block)(void);

void test() { int age = 10; Block = ^{// the value of age is captured in NSLog(@)"age is %d", age);
    };
    age = 20;
    block();
}

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

Print the result

10

__test_block_impl_0 has a member variable age, where age(_age) is equivalent to assigning _age to age. So when you define a block, you’ve already assigned the age value to a member variable inside the block. Just assign it to the outside age, execute block(), and the printed value will still capture the age value of 10.

// teststruct __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; }}; 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); }Copy the code

Static variables

void test() { int age = 10; static int height = 10; Block = ^{// the value of age is captured in NSLog(@)"age is %d, height is %d", age, height);
    };
    
    age = 20;
    height = 20;
}
Copy the code

Print the result

age is 10, height 20

Why is height 20? View c++ source code

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)); }Copy the code

The block captures *height, which is the address to which the pointer points, so height prints 20

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

From a lifecycle point of view, age is destroyed by the time the code executes to the block, so it is impossible to capture the memory address of age, as static does


The global variable

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(a); block();return0; }}Copy the code

Print the result

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

Looking at the c++ source code, you can see that global variables are not captured

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_); }Copy the code

About the self

See if self gets caught by the 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 
Copy the code

Looking at the c++ source code, self is captured, and when self. Name, [self name] is called, self is still captured

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")));
    }
Copy the code

– (void)test and – (instancetype)initWithName:(NSString *)name; self is passed as an argument, so it is a local variable. Local variables are captured by blocks.** Methods always take two implicit arguments self and _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;
}
Copy the code

conclusion

  • Block underlying structure

  • To ensure proper access to external variables within a block, a block has a capture mechanism