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