今天和同事在处理一处用strong
声明的Block属性引发的问题时偶然发现的。在诸多教程中都会讲到:声明属性时用strong
或者retain
效果是一样的(貌似更多开发者更倾向于用strong
)。不过在声明Block时,使用strong
和retain
会有截然不同的效果。strong
会等于copy
,而retain
竟然等于assign
!
当然定义Block还是应该用copy
(还有其他需要注意的地方,可以参考这篇文章:iOS: ARC和非ARC下使用Block属性的问题),因为非ARC下不copy
的Block会在栈中,ARC中的Block都会在堆上的。
可以这样复现问题。在非ARC环境下,定义一个简单类型,定义一个Block属性,先用正确的copy
:
1 @interface TestCls : NSObject 2 3 @property (nonatomic, copy) void(^myBlock)(); 4 5 @end
在另一个类型里声明变量:
1 TestCls *_testObj
然后在一个方法里,比如viewDidLoad
中,设置Block变量,注意即便是在非ARC下,没有引用外部变量的Block类型也是__NSGlobalBlock__
,而引用外部变量的Block才是__NSStackBlock__
,如下代码:
1 _testObj = [[TestCls alloc] init]; 2 3 int outerVar = 12; 4 5 _testObj.myBlock = ^void() 6 { 7 NSLog(@"Block被调用:%d", outerVar); 8 }; 9 NSLog(@"Block类型:%@", [_testObj.myBlock class]);
然后在另一个方法里(比如UIButton的点击事件方法里)去执行Block,如下:
1 _testObj.myBlock();
测试环境是Xcode 6/iOS 7/8,如果最上面myBlock
属性声明是copy
或者strong
的话,Block都会被copy
,输出:
Block类型:__NSMallocBlock
__ Block被调用:12
如果上面myBlock
属性声明是retain
或者assign
的话,Block表现起来就是assign
,没有去copy
,输出:
Block类型:__NSStackBlock__
此时可能会Crash(BAD_ACCESS
),也有可能输出错误的值(我在控制台下测试会出现这种情况)。因为Block作用域在函数栈里,而函数已经执行完毕了。
总之,strong
和retain
竟然有不一样的地方,而声明Block属性请务必用copy
。