block的本质:
- block本质上也是一个OC对象,它内部也有isa指针。
- block是封装了函数以及函数调用环境的OC对象。
- block底层结构如下图:
block的变量捕获:
- 局部变量:
auto:值捕获。
static:指针捕获。 - 全局变量:直接访问。
block的类型:
- NSGlobalBlock:
在内存的数据区域.data区,是没有访问auto局部变量。当调用copy后,没有任何的改变,依然在数据区域。 - NSStacklBlock:
在内存的栈中,是访问了auto局部变量。当调用copy后,从栈中复制到堆中。 - NSMallocBlock:
在内存的堆中,NSStacklBlock调用了copy。当调用copy后,引用计数器+1。
block的copy:
- 在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上。
block属性的建议写法:
@property (strong, nonatomic) void (^block)(void);
@property (copy, nonatomic) void (^block)(void);
- 在MRC环境下,在栈上的block要进行copy操作,复制到堆上。
block属性的建议写法:
@property (copy, nonatomic) void (^block)(void);
对象类型的auto局部变量:
- 如果block是在栈上:
不会对auto局部变量产生强引用。 - 如果block被copy到堆上:
1.1 会调用block内部的copy函数
1.1.1 copy函数内部会调用_Block_object_assign函数
1.1.1.1 _Block_object_assign函数会根据auto变量的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用。 - 如果block从堆上移除
1.1 会调用block内部的dispose函数
1.1.1 dispose函数内部会调用_Block_object_dispose函数
1.1.1.1 _Block_object_dispose函数会自动释放引用的auto变量(release)。
__block修饰符:
添加__block的变量,编译器会将该变量包装成一个对象,如下图: 包装后- 作用:
__block可以用于解决block内部无法修改auto 局部变量值的问题。(因为auto局部变量在block中是值传递捕获,auto局部变量是在栈中,程序执行完该作用域,就会被销毁,所以需要通过__block把auto局部变量放在堆中,当auto变量加上__block修饰符时,会把该变量封装成一个block结构体,在这个结构体中有个forwarding指针,指向自身,并且这个结构体中包含有auto变量) - 注意:
__block不能修饰全局变量、静态变量(static)。
对象类型的__block局部变量
- 如果__block是在栈上:
不会对指向的对象产生强引用。 - 如果__block被copy到堆上:
1.1 会调用__block内部的copy函数
1.1.1 copy函数内部会调用_Block_object_assign函数
1.1.1.1 _Block_object_assign函数会根据__block变量的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用。(注意:这里仅限于ARC时会retain,MRC时不会retain) - 如果block从堆上移除:
1.1 会调用block内部的dispose函数
1.1.1 dispose函数内部会调用_Block_object_dispose函数
1.1.1.1 _Block_object_dispose函数会自动释放引用的__block变量(release)。 - __block的_forwarding指针: _forwarding指针
block循环引用:
对象持有block,block中又持有对象自己,会造成对象和block无法释放。
解决方案:
- 在ARC中
方法一:可以使用__weak修饰符(不会产生强引用,指向的对象销毁时,会自动让指针置为nil)。
方法二:可以使用__unsafe_unretained修饰符(不会产生强引用,不安全,指向的对象销毁时,指针存储的地址值不变)。
__unsafe_unretained修饰符方法三:可以使用__block修饰符(必须调用block,并在执行完block中的内容后,把修饰的该对象置为nil)。
__block修饰符- 在MRC中(在MRC情况下,不支持弱引用__weak)
方法一:可以使用__unsafe_unretained修饰符(不会产生强引用,不安全,指向的对象销毁时,指针存储的地址值不变)。
方法二:可以使用__block修饰符(因为在MRC中,__block不会对持有对象进行强引用retain)。
__block修饰符
网友评论