大家对循环引用问题应该有很强的意识,所以我们一般的在使用block的时候特别注意循环引用,通常都是_ _weak.
Masonry框架相信对于一个iOS开发者来说应该很熟悉吧,但是Masonry的mas_makeConstraints方法里的block却直接使用self,却不会引起循环引用,这是为什么呢?
首先要明白block产生循环引用的原因:
block任何时候都会强引用在block代码块内部的对象,block消失,则强引用消失,block一直留存,强引用一直在,所以问题的关键是block是否会消失,如果A对象直接或者间接强引用一个block,block正好又强引用对象,那么就产生的循环引用。
masonry解决的办法是释放block.
其实这个block是个栈block,栈block有个特性就是它执行完毕之后就出栈,出栈了就会被释放掉。看mas_makexxx的方法实现会发现这个block很快就被调用了,完事儿就出栈销毁,构不成循环引用,所以可以直接放心的使用self。
拓展:block的种类
Objective-C 中 Block 有三种类型:
NSStackBlock存储于栈区
NSGlobalBlock存储于程序数据区
NSMallocBlock存储于堆区
NSGlobalBlock
block 内部没有引用外部变量的 Block 类型都是 NSGlobalBlock 类型,存储于全局数据区,由系统管理其内存,retain、copy、release操作都无效。
NSStackBlock
block 内部引用外部变量,retain、release 操作无效,存储于栈区,变量作用域结束时,其被系统自动释放销毁。支持copy,copy之后生成新的NSMallocBlock类型对象。
NSMallocBlock
如上例中的_block,[blockA copy]操作后变量类型变为 NSMallocBlock,支持retain、release,虽然 retainCount 始终是 1,但内存管理器中仍然会增加、减少计数,当引用计数为零的时候释放(可多次retain、release 操作验证)。copy之后不会生成新的对象,只是增加了一次引用,类似retain,尽量不要对Block使用retain操作
事例:
@property(nonatomic,copy)void(^block)();
intvalue =10;
void(^blockA)() = ^{
NSLog(@"value: %d",value);
};
NSLog(@"ARC 引用计数: %ld, block is: %@",CFGetRetainCount(((__bridgeCFTypeRef)blockA)), blockA);
void(^blockB)() = ^{
NSLog(@"blockB")
; };
NSLog(@"ARC 引用计数: %ld, block is: %@",CFGetRetainCount(((__bridgeCFTypeRef)blockB)), blockB);
_block = blockA;
NSLog(@"ARC 引用计数: %ld, block is: %@",CFGetRetainCount(((__bridgeCFTypeRef)_block)), _block);
打印结果:
ARC 引用计数:1, blockis: <__NSMallocBlock__:0x6080000536b0>
ARC 引用计数:1, blockis: <__NSGlobalBlock__:0x106bc7140>
ARC 引用计数:1, blockis: <__NSMallocBlock__:0x6080000536b0>
block类型转换
intvalue =10;
NSLog(@"%@",^{
NSLog(@"value: %d",value);
});
void(^blockA)() = ^{
NSLog(@"value: %d",value);
};
NSLog(@"ARC 引用计数: %ld, block is: %@",CFGetRetainCount(((__bridgeCFTypeRef)blockA)), blockA);
打印结果<__NSStackBlock__: 0x7fff592aebd8>
ARC 引用计数: 1, block is: <__NSMallocBlock__: 0x6180000487f0>
由此看出,block 变量在引用外部变量的时候系统自动将其拷贝到堆区了,造成我们看到变量 blockA 是 NSMallocBlock 类型