底层结构
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对象
自动变量
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底层结构
-
为了保证block内部能够正常访问外部变量,block有个捕获机制