问题:
Masonry block 中直接用self不会有循环引用
RAC subscribeNext 中直接用self会有循环引用
Masonry 例:
[self.imageView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.edges.mas_equalTo(self);
}];
RAC 例:
[self.preViewContainer addGestureRecognizer:tapPreImageGesture];
[[tapPreImageGesture rac_gestureSignal] subscribeNext:^(id x) {
NSInteger preIndex = 0;
preIndex = self.items.count - 1;
}];
原因:
关键点在于block是否作为实例的一个属性被保存(copy),Masonry中的block只是被执行,没有人retain这个block,但是RAC中的block参数会被copy一份作为属性保存起来,RAC的singal会retain这个block,所以RAC的block直接用self会造成循环引用。
block中用到实例变量也会产生循环引用
保险的做法是block中都用weakSelf,或者在里面strongify一下,block中不要用实例变量。
另外还有一个小坑,参考下面第一个回答。
https://stackoverflow.com/questions/24720646/memory-management-in-reactivecocoa
UITextField *textField = self.searchText;
__weak typeof(self) weakSelf = self;
[[self.searchText.rac_textSignal map:^id(NSString *text) {
return [weakSelf isValidSearchText:text] ? [UIColor whiteColor] : [UIColor yellowColor];
}] subscribeNext:^(UIColor *color) {
textField.backgroundColor = color;
}];
这种情况下虽然textField是局部变量,但是还会造成循环引用,self虽然会被释放,但是textField永远不会被释放,解决办法是weakify这个textField
textField -> textSignal -> mapped signal
^ ^ |
| | |
| +------------- block <---+
|
self
正确写法:
UITextField *textField = self.searchText;
@weakify(self, textField);
[[self.searchText.rac_textSignal map:^id(NSString *text) {
@strongify(self);
return [self isValidSearchText:text] ? [UIColor whiteColor] : [UIColor yellowColor];
}] subscribeNext:^(UIColor *color) {
@strongify(textField);
textField.backgroundColor = color;
}];
block造成循环引用原因总结:
- self引用block,block强引用self
- self引用block,block引用self中的实例变量(没有显式调用self)
- self中的局部变量引用block,block引用局部变量,这个局部变量不会被释放,RAC中比较常见,如以上最后一个例子
block中什么时候可以直接用self,什么时候不能直接用self?
看self有没有直接或间接强引用这个block(有copy操作),如果有的话则不能直接用self,没有的话可以直接用self