本帖最后由 竹林风 于 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正常释放,否则,证明内存泄露了。
[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
在项目开发中,个人建议最好为了统一,还是按照普通写法即情况二的写法。
有什么不正确的地方希望坛友指出哦!
|