http://www.sufeinet.com/plugin.php?id=keke_group

苏飞论坛

 找回密码
 马上注册

QQ登录

只需一步,快速开始

分布式系统框架(V2.0) 轻松承载百亿数据,千万流量!讨论专区 - 源码下载 - 官方教程

HttpHelper爬虫框架(V2.7-含.netcore) HttpHelper官方出品,爬虫框架讨论区 - 源码下载 - 在线测试和代码生成

HttpHelper爬虫类(V2.0) 开源的爬虫类,支持多种模式和属性 源码 - 代码生成器 - 讨论区 - 教程- 例子

查看: 3590|回复: 6

[语法] iOS ARC下用块(block)的循环引用问题

[复制链接]
发表于 2018-12-10 17:12:47 | 显示全部楼层 |阅读模式
本帖最后由 竹林风 于 2018-12-10 17:10 编辑

在项目开发中,我们都会用到block,同样也会面临ARC下产生循环引用的问题,那么造成泄露的原因是什么呢?

例如:

[Objective-C] 纯文本查看 复制代码
[self.fruit requestData:^(id data) {
    self.name = @"case";
}];


这种情况是最常见的循环引用导致的内存泄露了,在这里,self强引用了fruit,fruit又强引用了一个block,而该block在回调时又调用了self.会导致block又强引用了self,造成了一个保留环,最终导致self无法释放。self -> fruit -> block -> self

一般性的解决方法

[Objective-C] 纯文本查看 复制代码
__weak typeof(self) weakSelf = self;
    [self.fruit requestData:^(id data) {
        typeof(weakSelf) strongSelf = weakSelf;
       strongSelf.name = @"case";
    }];


通过__weak的修饰,先把self弱引用(默认是强引用,实际上self是有个隐藏的__strong修饰的),然后在block回调里用weakSelf,这样就会打破保留环,从而避免了循环引用,如下:
self -> teacher -> block -> weakSelf
一般会在block回调里再强引用一下weakSelf(typeof(weakSelf))strongSelf = weakSelf;因为__weak修饰的都是存在栈内,可能随时会被系统释放,造成后面调用weakSelf的时候weakSelf可能已经是nil了,后面用weakSelf调用任何代码都是无效的。
可以通过一个demo说明哪些情况下有泄露
虽然说用block时会产生循环引用,但并不是所有情况下都会有内存泄露的问题。
  • 进入demo,点击跳转button,跳转到SecondVC,
  • 在SecondVC中有六种情况的Button,每点击一个会触发一个block。
  • 点击返回,如果执行了dealloc,证明SecondVC正常释放,否则,证明内存泄露了。

2.gif

[Objective-C] 纯文本查看 复制代码
//情况一:不泄露[/color]
[color=#252525]- (void)case1[/color]
[color=#252525]{[/color]
[color=#252525]    NSLog(@"case 1 Click");[/color]
[color=#252525]    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{[/color]
[color=#252525]        self.name = @"case 1";[/color]
[color=#252525]    });[/color]
[color=#252525]}[/color]

[color=#252525]//情况二:不泄露[/color]
[color=#252525]- (void)case2[/color]
[color=#252525]{[/color]
[color=#252525]    NSLog(@"case 2 Click");[/color]
[color=#252525]    __weak typeof(self) weakSelf = self;[/color]
[color=#252525]    [self.fruit requestData:^(id data) {[/color]
[color=#252525]        typeof(weakSelf) strongSelf = weakSelf;[/color]
[color=#252525]        strongSelf.name = @"case 2";[/color]
[color=#252525]    }];[/color]
[color=#252525]}[/color]

[color=#252525]//情况三:泄漏[/color]
[color=#252525]- (void)case3[/color]
[color=#252525]{[/color]
[color=#252525]    NSLog(@"case 3 Click");[/color]
[color=#252525]    [self.fruit requestData:^(id data) {[/color]
[color=#252525]        self.name = @"case 3";[/color]
[color=#252525]    }];[/color]
[color=#252525]}[/color]

[color=#252525]//情况四:不泄露[/color]
[color=#252525]- (void)case4[/color]
[color=#252525]{[/color]
[color=#252525]    NSLog(@"case 4 Click");[/color]
[color=#252525]    [self.fruit requestData:^(id data) {[/color]
[color=#252525]        self.name = @"case 4";[/color]
[color=#252525]        self.fruit = nil;[/color]
[color=#252525]    }];[/color]
[color=#252525]}[/color]

[color=#252525]//情况五:不泄露[/color]
[color=#252525]- (void)case5[/color]
[color=#252525]{[/color]
[color=#252525]    NSLog(@"case 5 Click");[/color]
[color=#252525]    FruitModel *f = [[FruitModel alloc] init];[/color]
[color=#252525]    [f requestData:^(id data) {[/color]
[color=#252525]        self.name = @"case 5";[/color]
[color=#252525]    }];[/color]
[color=#252525]}[/color]

[color=#252525]//情况六:不泄露[/color]
[color=#252525]- (void)case6[/color]
[color=#252525]{[/color]
[color=#252525]    NSLog(@"case 6 Click");[/color]
[color=#252525]    [self.fruit callCase6BlackEvent];[/color]
[color=#252525]    self.fruit.case6Block = ^(id data) {[/color]
[color=#252525]        self.name = @"case 6";[/color]
[color=#252525]        [/color]
[color=#252525]        //下面两句代码任选其一即可防止内存泄漏,self.teacher 或者 case6Block 置为空都可以打破 retain cycle[/color]
[color=#252525]        self.fruit = nil;[/color]
[color=#252525]        //        self.teacher.case6Block = nil;[/color]
[color=#252525]    };[/color]
[color=#252525]}


分析:

  • 情况一:执行了dealloc,不泄露,此情况虽然是block,但未形成保留环block ->self
  • 情况二:执行了dealloc,不泄露,此情况就是内存泄漏后的一般处理了。self ->fruit ->block ->strongSelf,后面那个strongSelf和原来的self并没有直接关系,因为strongSelf是通过weakSelf得来的,而weakSelf又没有强引用原来的self
  • 情况三:未执行dealloc,内存泄漏,此情况就是最典型的循环引用了,形成保留环无法释放,self ->fruit ->block ->self
  • 情况四:执行了dealloc,不泄露,虽然也是保留环,但通过最后一句,使self不再强引用fruit,打破了保留环
  • 情况五:执行了dealloc,不泄露,未形成保留环 t ->block ->self
  • 情况六:执行了dealloc,不泄露,最后两句代码任选其一即可防止内存泄漏,self.fruit 或者 case6Block 置为空都可以打破 retain cycle


在项目开发中,个人建议最好为了统一,还是按照普通写法即情况二的写法。

有什么不正确的地方希望坛友指出哦!

Demo附件: Test-BlockRetainCycle.zip (168.18 KB, 下载次数: 0)


1. 开通SVIP会员,免费下载本站所有源码,不限次数据,不限时间
2. 加官方QQ群,加官方微信群获取更多资源和帮助
3. 找站长苏飞做网站、商城、CRM、小程序、App、爬虫相关、项目外包等点这里
 楼主| 发表于 2018-12-16 13:11:21 | 显示全部楼层

iOS中Block的理解和用法
http://www.sufeinet.com/thread-24329-1-1.html
(出处: 苏飞论坛)
发表于 2018-12-10 17:19:05 | 显示全部楼层
强烈支持楼主ing……
发表于 2018-12-10 17:43:06 | 显示全部楼层
强烈支持楼主ing……
发表于 2018-12-11 14:08:45 | 显示全部楼层
我只是路过打酱油的。
发表于 2018-12-11 14:21:12 | 显示全部楼层
真是难得给力的帖子啊。
发表于 2018-12-11 16:05:38 | 显示全部楼层
无回帖,不论坛,这才是人道。
您需要登录后才可以回帖 登录 | 马上注册

本版积分规则

QQ|手机版|小黑屋|手机版|联系我们|关于我们|广告合作|苏飞论坛 ( 豫ICP备18043678号-2)

GMT+8, 2024-11-8 13:38

© 2014-2021

快速回复 返回顶部 返回列表