Block产生的内存泄露,以及解决方法
2016-03-30 10:21
441 查看
前言:
在ARC(自动引用技术)前,Objective-c都是手动来分配释放 释放 计数内存,其过程非常复杂。
ARC技术推出后,貌似世界和平了很多,但是其实ARC并不等同于Java或者C#中的垃圾回收,ARC计数只是在XCode在编译的时候自动帮我们加上了释放 计数+1 计数-1.
内存泄露例子:
然而在一些特殊的情况下,内存泄露依然存在,而且防不慎防,这里讲一下Objective-C中Block计数是如何产生内存泄露的,如下代码
.h中
.m中
main函数中
上面的程序看似没有问题,但是实际上对象b永远无法释放,原因在于doAction函数,这个函数里面有一个block函数名为completionBlock ,也就是一个函数指针。这个函数指针在调用的时候有使用一个对象,也就是self对象。但是这个block隐形的做了一件事情——将self引用计数+1了,因此这个时候self对象(也就是main函数中的b对象)的引用计数是2,这个时候即使我执行了b=nil,也无法释放,因为b=nil只是将计数减1了,而真正释放的唯一条件是引用计数为0。这就是所谓的Block的循环引用。
如何解决:
所以在使用block技术的时候,需要格外小心。有几个解决方法
approach 1: 让block里面的self的引用计数不要+1,这个时候做法是将" __block B *b1 = self;"这一行改为," __weak __block B *b1 = self;",表示说“我block里面虽然会用到self,但是别担心,我不会讲引用计数+1的”
approach 2:在doAction函数内存的最后一行添加 self.completionBlock=nil; 因为block内部将self计数+1了,但是如果这个block自己先消亡,那么与之相关的一切都讲消亡(当然对于引用计数大于1的对象,不会消亡,只会计数减1)。
附加:
PS:开发中,几乎每个.m文件都会用到block技术,但是从未发现和在意这个内存泄露问题,这并不是XCode编译时的优化,而是我们所用到的Block技术(例如AFNetwork GCD Animation)中的block都是匿名Block——即,用完自动释放。 如果有一天不用匿名block就需要注意这个问题了。
例如下面的例子中,虽然使用了Block,但是没有泄露,是因为这是一个匿名的Block(即匿名函数指针)
在ARC(自动引用技术)前,Objective-c都是手动来分配释放 释放 计数内存,其过程非常复杂。
ARC技术推出后,貌似世界和平了很多,但是其实ARC并不等同于Java或者C#中的垃圾回收,ARC计数只是在XCode在编译的时候自动帮我们加上了释放 计数+1 计数-1.
内存泄露例子:
然而在一些特殊的情况下,内存泄露依然存在,而且防不慎防,这里讲一下Objective-C中Block计数是如何产生内存泄露的,如下代码
.h中
typedef void (^CompletionBlock)(NSString *aStr); @interface B : NSObject @property (copy) CompletionBlock completionBlock; @property (copy) NSString *str; @end
.m中
@implementation B -(id)init{ self = [super init]; if(self){ self.str = @"init string value"; } return self; } -(void)doAction { __block B *b1 = self; self.completionBlock = ^(NSString *aStr){ b1.str = aStr; }; self.completionBlock(@"new string value"); } -(void)dealloc{ NSLog(@"dealloc B"); } @end
main函数中
B *b = [[B alloc]init]; [b doAction]; b = nil;//这句有和无其实无所谓
上面的程序看似没有问题,但是实际上对象b永远无法释放,原因在于doAction函数,这个函数里面有一个block函数名为completionBlock ,也就是一个函数指针。这个函数指针在调用的时候有使用一个对象,也就是self对象。但是这个block隐形的做了一件事情——将self引用计数+1了,因此这个时候self对象(也就是main函数中的b对象)的引用计数是2,这个时候即使我执行了b=nil,也无法释放,因为b=nil只是将计数减1了,而真正释放的唯一条件是引用计数为0。这就是所谓的Block的循环引用。
如何解决:
所以在使用block技术的时候,需要格外小心。有几个解决方法
approach 1: 让block里面的self的引用计数不要+1,这个时候做法是将" __block B *b1 = self;"这一行改为," __weak __block B *b1 = self;",表示说“我block里面虽然会用到self,但是别担心,我不会讲引用计数+1的”
approach 2:在doAction函数内存的最后一行添加 self.completionBlock=nil; 因为block内部将self计数+1了,但是如果这个block自己先消亡,那么与之相关的一切都讲消亡(当然对于引用计数大于1的对象,不会消亡,只会计数减1)。
附加:
PS:开发中,几乎每个.m文件都会用到block技术,但是从未发现和在意这个内存泄露问题,这并不是XCode编译时的优化,而是我们所用到的Block技术(例如AFNetwork GCD Animation)中的block都是匿名Block——即,用完自动释放。 如果有一天不用匿名block就需要注意这个问题了。
例如下面的例子中,虽然使用了Block,但是没有泄露,是因为这是一个匿名的Block(即匿名函数指针)
相关文章推荐
- MySQL validate_password 插件
- javascript设计模式(一)--面向对象
- linux kernel的中断子系统之(九):tasklet
- 光晶片
- form表单的多种提交方式
- 单工、半双工、全双工通信
- VMware SDS 之二 : VSAN用在哪?
- Random的setSeed方法
- 滚动轴承特征频率计算推导过程
- Android--activity获取返回值
- Linux权限360度赤裸裸华丽丽大曝光连载之一:从ls开始
- fedora 15 iso 硬盘安装
- Systemd 进程管理器
- sed 正则表达式
- 黄瓜涂上避孕药吃了会绝育-搜狐社区
- CentOS 6 初步计划有望5月发布
- 中科大Linux镜像源正式面向全国用户开张
- Intel揭幕3D晶体管,新处理器系列产生
- 30年前一个关于中国、美国教育的预言
- 中国白领过劳现象调查:商务楼里的黑砖窑童工