剖析RAC中的@weakify、@strongify
需要:pod 'YYKit'
在block语句块中,如果需引用self,而self对象中又持有block对象,就会造成循环引用循环引用(retain cycle)
,导致内存泄露,比如以下代码
self.block = ^{
[self description];
};
一般我们是这么解决的,使用一个__weal
修饰的weakSelf变量指向self对象,在block中使用weakSelf:
__weak typeof(self) weakSelf = self; self.block = ^{
[weakSelf description]; };
但是酱紫写,还是可能出问题,因为weakSelf是弱引用,而self一旦释放了,weakSelf可能为nil,还是举个栗子吧:
先定义一个TestObj对象,他的属性有一个block对象
@interface TestObj : NSObject@property (nonatomic, copy)void(^block)();@end@implementation TestObj- (void)dealloc { NSLog(@"%s",__func__);}- (instancetype)init { self = [super init]; if (self) { __weak typeof(self) weakSelf = self; self.block = ^{ dispatch_async(dispatch_get_global_queue(0, 0), ^{ [NSThread sleepForTimeInterval:1]; NSLog(@"%@",weakSelf); }); }; } return self; } @end
执行testFunc
方法,结果是打印的是(null),因为block里打印的方法是异步执行的,在 NSLog(@"%@",weakSelf);
这句代码执行之前testFunc
函数就结束,所以obj
对象已经被release了。
怎么解决呢?所以再对weakSelf
做一次 __strong
就可以了:
__weak typeof(self) weakSelf = self; self.block = ^{ __strong typeof(weakSelf) strongSelf = weakSelf; dispatch_async(dispatch_get_global_queue(0, 0), ^{ [NSThread sleepForTimeInterval:1]; NSLog(@"%@",strongSelf); }); };
}
使用了__strong
在strongSelf
变量作用域结束之前,对weakSelf
有一个引用,防止对象(self)提前被释放。而作用域一过,strongSelf
不存在了,对象(self)也会被释放。
前面的写法虽然严谨了,也解决了问题了,但是作为喜欢偷懒的程序猿,会不会觉得很啰嗦?每次都要写那两条长长的__weak
和__strong
,而且在block里用到的self的全部要改成strongSelf,假设把一段很多self的代码拷贝到block里,一个个改成strongSelf是不是很蛋疼?
只要在block外用了@weakify(self);然后再block里写@strongify(self);就可以了,@strongify(self);语句后的的self可以原封不动,好像很神奇,下面一起看看@weakify、@strongify 这两个神奇的宏最终替换了什么东西。
导入RAC的头文件,把上面的测试代码替换成RAC中用的@weakify(self);和@strongify(self), 分屏显示Xcode,让右侧的显示内容改为 preprocess“,就可以看到宏最终替换的结果。
再比如:
@weakify(self); _topView.block = ^(NSInteger tag) { @strongify(self); CGPoint point = CGPointMake(tag * SCREEN_WIDTH, self.scrollView.contentOffset.y); [self.scrollView setContentOffset:point animated:YES]; };