在iOS的开发中,block是比较频繁的一个使用的功能。可是我们都知道,block如果使用不当,很容易引起循环引用,造成内存泄漏。今天为大家介绍几种解除block循环引用的方式,愉快的用起来吧!
我相信作为一个有一定开发经验的人都知道,想要防止循环引用,最关键的就是要打破引用链,即让两个相互强引用的对象,让其中一方变为弱引用。那我们的第一种方式,就是这种最常用到的,请看下面的代码:
self.block = ^(NSString *name) {
self.name = name;
};
self.block(@"name1");
上面的代码明显会造成内存泄漏,self和block之间互相强引用,我们的解决方法是,将block对self的引用变为弱引用:
__weak typeof(self) weakSelf = self;
self.block = ^(NSString *name) {
weakSelf.name = name;
};
self.block(@"name1");
很多时候,这样写完你是不是就结束了?NONONO!确实,这样已经打破了循环引用,但是存在一个风险:由于block内部对self是弱引用,假如block内有大量耗时操作,在block没有执行完成之前,weakSelf即已经被释放,那会发生什么呢?没错,程序会直接crash掉,这一点大家可以自己验证一下。
解决的办法就是,block内将weakSelf强引用!哈哈,是不是有点绕,看代码:
__weak typeof(self) weakSelf = self;
self.block = ^(NSString *name) {
__strong typeof(self) strongSelf = weakSelf;
strongSelf.name = name;
};
self.block(@"name1");
如此一来,哪怕外部调用者self已经被释放,block内的逻辑也会从容的执行完成,所以我推荐大家开发中都添加上这两步转化。这样weak和strong转来转去的画面,是不是像极了翩翩起舞的男男女女?weak & strong dance由此得名O(∩_∩)O哈哈~
上面说了,打破循环引用,就是让block弱引用self,但是谁规定的block里面一定要有self呢?我们有没有可能通过在block中不直接使用self来解除引用呢?答案是肯定的!
在讲解第二种方式之前,得先有点小的知识储备:
我们知道,block默认是会对block之外的变量进行值拷贝,而__block这个变量修饰符,可以允许block内部修改在block之外定义的变量的值。至于具体的原理,网上有很多文章已经讲的很透彻,感兴趣的朋友可以去查找。接下来我们的方案就是基于这个__block,上代码:
// 假设这里的self是Person类
__block Person *p = self;
self.block = ^(NSString *name) {
p.name = name;
p = nil; //最后释放掉p变量
};
self.block(@"name1");
如上,我们通过__block修饰了变量p(这里我们假设是在Person类中),这样在block内部,就可以使用p对对应的值进行设置或者进行函数调用。最终再将p置为nil,释放p的内存空间。这样就通过不直接使用self的方式,巧妙的避免的循环引用。
其实方法三的思考和方法二有点类似。我们的目的都是在不使用self的情况下,还要完成对self的属性存取或者函数调用。方法二是用另一个变量来指向self,那我们为什么不直接把self作为一个参数,传进block内部供使用呢?想到了这一点,我们就可以这么做:
self.block = ^(Person *p, NSString *name) {
p.name = name;
};
self.block(self, @"name1");
以上几种方式都可以达到解除循环引用的目的,具体怎么选择完全在自己。如果内容有误,或者你有更好的实现方式,欢迎与我交流哦!也可以关注我的微信公众号:iOS进化论,目前在做SwiftUI的内容,感兴趣的童鞋可以来看看哦!