Block相关
block本质是一个OC对象,内部也有isa指针,封装了函数调用以及函数调用环境
- isa 指针,所有对象都有该指针,用于实现对象相关的功能。
- flags,用于按 bit 位表示一些 block 的附加信息,本文后面介绍 block copy 的实现代码可以看到对该变量的使用。
- reserved,保留变量。
- invoke,函数指针,指向具体的 block 实现的函数调用地址。
- descriptor, 表示该 block 的附加描述信息,主要是 size 大小,以及 copy 和 dispose 函数的指针。
- variables,capture捕获 过来的变量,block 能够访问它外部的局部变量,就是因为将这些变量(或变量的地址)复制到了结构体中。
变量捕获
auto 默认的变量申明关键字,与static相对
三种block
- NSGlobalBlock 全局的静态 block,不会访问任何auto变量。
- NSStackBlock 保存在栈中的 block,访问了auto变量,当函数返回时会被销毁。
- NSMallocBlock 保存在堆中的 block,NSStackBlock调用了copy变成NSMallocBlock, 当引用计数为 0 时会被销毁。
__block
- block可以用于解决block内部无法修改auto变量值问题
- block不能修饰全局变量、静态变量(static)
- 编译器会将__block变量包装成一个对象
修饰词用copy
没有copy操作,就不会在堆上,无法控制生命周期。
block内存管理
-
当block在栈上时,不会对__block变量, 对象类型的auto变量产生强引用
-
当block被copy到堆时
- 会调用到block内部的copy函数
- copy函数会调用_Block_object_assign函数
- _Block_object_assign函数会对__block变量形成强引用(retain)
-
当block从堆中移除时
- 会调用block内部的dispose函数
- dispose函数内部会调用_Block_object_dispose函数
- _Block_object_dispose函数会自动释放引用的__block变量(release)
__block修饰对象
-
当__block变量在栈上时,不会对指向的对象产生强引用
-
当__block变量被copy到堆时
-
会调用__block内部的copy函数
-
copy函数内部会调用_Block_object_assign函数
-
_Block_object_assign函数会根据所指向对象的修饰符(__Strong、__weak、__unsafe_unretained)做出相应操作,形成强引用(retain)或者弱引用
注意 这里仅限于ARC时会retain,MRC时不会retain
-
-
当__block变量从堆上移除时
- 会调用__block内部的dispose函数
- dispose函数内部会调用_Block_object_dipose函数
- _Block_object_dipose函数会自动释放指向的对象(release)
解决循环引用
MRC
- 用__unsafe_unretained
- 用__block解决
ARC
-
__unsafe_unretained (不会产生强引用,不安全,指向的对象销毁时,指针存储的地址值不变)
__unsafe_unretained typeof(self) weakSelf = self;
-
__weak (不会产生强引用,指向的对象销毁时会自动将指针置为nil)
__weak typeof(self) weakSelf = self;
-
__block (必须调用block,并将__block修饰的变量置为nil)
__block Person *p = [[Person alloc] init]; p.age = 20; p.block = ^{ NSLog(@"age is %zd", p.age); p = nil; }; p.block();