美文网首页
weak-strong dance探究

weak-strong dance探究

作者: conowen | 来源:发表于2018-04-09 11:17 被阅读7次

循环引用

循环引用是iOS开发常见的问题,虽然现在普遍是ARC工程,但是这个问题仍然无可避免。一般都是两个强引用对象互相持有对方,导致两个对象都无法被正常销毁,也就是说,引用计数始终无法为0,系统无法回收内存,导致内存泄露。

block循环引用

@property (copy,nonatomic) void(^block)(void);
...
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    self.block = ^{
        NSLog(@"self =%@",self);
    };
}

这时候编译器会提示
Capturing 'self' strongly in this block is likely to lead to a retain cycle
这就是典型的block循环引用问题。
因为self持有block,而block捕获self变量,也持有self。也就是互相持有,导致这个ViewController的不能销毁释放,就是dealloc无法被调用。
注意,就算此时block没有执行,只要定义并且创建了这个block,block就会捕获外部变量self,就会导致了retain cycle问题。

weak-strong dance

解决这个问题,苹果官方推荐了一种方法,就是使用以下方法

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    __weak typeof(self) w = self;
    self.block = ^{
        typeof(self) s = w;
        NSLog(@"self =%@",s);
    };
}

原理简单来说就是在block外面把self强行转换为weak类型,然后block执行的时候,又强行转换为strong。如果是单纯地解决retain cycle问题的话,在block声明__weak typeof(self) w = self;即可,那为什么还需要在block里面又强引用一次self呢?typeof(self) s = w;,这样不是导致self又无法释放销毁了吗?

因为s是一个局部变量,生命周期只是在block执行期间,一旦block执行完毕,里面强引用的s就会被释放销毁,self自然就销毁了。这么做的主要用途是防止在执行block期间,self被释放销毁了。万一block执行代码有需要用到self的话,self被置为nil,就执行不了了。

这里需要明确,这是在block执行期间,typeof(self) s = w;这句代码执行前,self一定要保证不能被销毁,如果被销毁置为nil,就算使用这句代码,s一样是为nil的。

@interface SecondViewController ()
@property (copy,nonatomic) void(^block)(void);
@end

@implementation SecondViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    __weak typeof(self) w = self;
    self.block = ^{
        typeof(self) s = w;
        NSLog(@"s0 =%@",s);
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"s1 =%@",s);
        });
    };
    self.block();
    NSLog(@"self.block()");

}
- (void)dealloc {
    NSLog(@"seconde deallo");
}

Log输出结果如下

2018-04-09 11:05:35.987263+0800 TestMRC[24989:4591265] s0 =<SecondViewController: 0x7fe295715680>
2018-04-09 11:05:35.987412+0800 TestMRC[24989:4591265] self.block()
2018-04-09 11:05:45.987931+0800 TestMRC[24989:4591265] s1 =<SecondViewController: 0x7fe295715680>
2018-04-09 11:05:45.988073+0800 TestMRC[24989:4591265] seconde deallo

如果想使得s有效,务必保证block要在self释放销毁之前操作,但是一般编程,这个不可控,所以还是得在block里面判断self是否为nil。

不是所有block都要加weak-strong dance

有些人,看到block都要加weak-strong dance代码,虽然没啥问题,但是也可不必加的。简单来说,一般block与self造成了循环引用才需要加。
例如以下
这些block是局部变量,随着作用域的结束,block的生命周期也会结束,不存在循环引用问题。

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"s =%@",self);
    });

    TestBlock *testBlock = [TestBlock new];
    testBlock.block = ^{
        NSLog(@"s =%@",self);
    };

所有权修饰符

  • __strong修饰符
    对象的默认修饰符,强引用

  • __weak修饰符
    弱引用

  • __unsafe_unretained修饰符
    __unsafe_unretained修饰符是不安全的修饰符,尽管ARC式的内存管理是编译器的工作,但附有__unsafe_unretained修饰符的变量不属于编译器的内存管理对象。__unsafe_unretained和__weak一样不能持有对象。

  • __autoreleasing修饰符

相关文章

网友评论

      本文标题:weak-strong dance探究

      本文链接:https://www.haomeiwen.com/subject/ouvpittx.html