问题:

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造成循环引用原因总结:

  1. self引用block,block强引用self
  2. self引用block,block引用self中的实例变量(没有显式调用self)
  3. self中的局部变量引用block,block引用局部变量,这个局部变量不会被释放,RAC中比较常见,如以上最后一个例子

block中什么时候可以直接用self,什么时候不能直接用self?

看self有没有直接或间接强引用这个block(有copy操作),如果有的话则不能直接用self,没有的话可以直接用self

posted on 2018-02-07 15:42  ximenchuixie  阅读(166)  评论(0编辑  收藏  举报