block的底层结构
main函数中定义了一个block
int main(int argc, const char * argv[]) {
@autoreleasepool {
int age = 10;
void (^block)(void) = ^(void){
NSLog(@"%d",age);
};
block();
}
return 0;
}
clang -rewrite-objc main.m执行后看看底层,把影响我们分析代码的强制类型转换全部删掉看更清晰
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
int age = 10;
void (*block)(void) = &__main_block_impl_0(
__main_block_func_0,
&__main_block_desc_0_DATA,
age);
block->FuncPtr(block);
}
return 0;
}
看下__main_block_impl_0的结构
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int age;
// 构造函数,返回结构体对象本身
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int flags=0) : age(_age) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
__main_block_func_0封装了block函数执行代码
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int age = __cself->age; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders__d_dpwms6wj1nqbjv0j93rmzl3h0000gn_T_main_fdaf48_mi_0,age);
}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
block就是一个对象,封装了函数执行代码和执行上下文
block的变量截获捕捉
全局变量、全局静态变量不截获
局部基本数据类型截获其值
局部静态变量截获其指针
对象类型截获对象及其所有权修饰符
__block修饰
需要给对象和局部变量赋值的时候,需要__block修饰,实质就是__block把他们包装成了一个新的对象_Block_byref_age_0, __forwarding指针指向自身
struct __Block_byref_age_0 {
void *__isa;
__Block_byref_age_0 *__forwarding;
int __flags;
int __size;
int age;
};
3种block的copy操作
被__block修饰的对象,修改这个对象的值时,其实就是通过栈上这个对象的forwarding的指针找到堆上的对象,然后修改它的值。
block的循环引用
第一个图MRC不会循环引用,ARC会循环引用,改成2图,ARC不会循环引用
一个对象的成员变量对block强引用,block对象内部又持有该对象,就会引起自循环引用,
把引用的改对象用__weak修饰,
__block修饰MRC不会循环引用
__ARC下需要把__block修饰的对象清空一下,并且调用这个block
一个block使用的注意点
//注意右边的返回值id要写上 ,要不返回nil会报错
id(^block)(void) = ^id() {
return nil;
};
block();