美文网首页
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