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

苏飞论坛

 找回密码
 马上注册

QQ登录

只需一步,快速开始

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

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

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

查看: 4500|回复: 5

[常见问题] 内存泄漏的几个常见情景

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

仅是个人意见,如有问题,欢迎指正,谢谢。

1.block循环引用


情景:

[Objective-C] 纯文本查看 复制代码
self.animations = ^{

self.testArray= @[@"test"];

    };


分析:

在上面的代码中:self对animations这个block强引用,block捕获了testArray对象,同样也对testArray持有强引用,而self对testArray也持有强引用。三者之间的引用:self -> animations -> testArray -> self。三者之间相互引用,引用计数无法变为0,导致内存泄漏。

解决方法:

1.最常用的方法是使用对self使用__weak修饰,__weak typeof(self)weakSelf = self,代码如下

[Objective-C] 纯文本查看 复制代码
__weak typeof (self) weakSelf = self; 

 self.animations = ^{

weakSelf.testArray= @[@"test"];

   };


weakSelf也还是万能的,如果在多线程下,在某一个线程中,对str做了一次release操作了,内存被销毁了,但是指针还没有设置nil。由于是多线程,在销毁的瞬间,同时又有其他线程在访问str这个对象导致crash。(如果没有出现crash,可以加大循环的次数,改成1000)。

[Objective-C] 纯文本查看 复制代码
__weak MyViewController *wself = self;    self.completionHandler = ^(NSInteger result) {    [wself.property removeObserver: wself forKeyPath:@"pathName"];};

但是使用__strongtypeof(weakSelf)strongSelf = weakSelf;这样一来,self所指向对象的引用计数变成2,即使主线程中的self因为超出作用域而释放,对象的引用计数依然为1,避免了对象的销毁。

[Objective-C] 纯文本查看 复制代码
__weak MyViewController *wself =self; self.completionHandler = ^(NSIntegerresult) {    __strong  typeof(wself) sself = wself;// 强引用一次    [sself.property removeObserver: sself forKeyPath:@"pathName"];};


2.delegate循环引用

情景:

控制器a,视图b是控制器a的子视图。b.delegate = a。

分析:

视图b是控制器a的子视图,a对b强引用,而b对delegate强引用。b.delegate = a,delegate对控制器a强引用。引用关系:a -> b -> delegate -> a。

解决方法:

b的delegate使用weak修饰:@property(nonatomic,weak)id<BDelegate> delegate;


注意:不能使用assign代理weak,assign是修饰数值类型的变量的,因为基本数值类型是存在栈的,不需要手动管理内存。如果拿assign去修饰一个对象,会导致内存被销毁了,但是指针没有被置为nil,这个对象就会变成野指针,下次访问这个野指针的时候,谁也无法知道这个野指针是访问 了什么样的内存,导致crash。


3.NSTimer没有销毁



[Objective-C] 纯文本查看 复制代码
[NSTimer timerWithTimeInterval:1 target:self selector:@selector(viewDidLoad) userInfo:nil repeats:YES];


系统为了保证定时器能够正常运行,会对target一直强引用,知道定时器结束。所以即使页面已经pop了,但是页面并没有被销毁。即使使用__weak typeof(self) weakSelf = self;target传weakSelf也没有用,因为系统会一直对这个target强引用。苹果的官方文档是这么说的:

2413774-250dcb2541676d10.png

那要怎么销毁呢?在不需要定时器的时候,关闭定时器,把定时器置nil

[_timer invalidate];//关闭定时器
  _timer = nil;//把定时器置nil
注意:不要在dealloc里面写着两句代码,因为_timer被循环引用着,压根就不会走到dealloc方法来。
4.非OC对象内存处理,没有手动释放
对于一些非OC对象,使用完毕后其内存仍需要我们手动释放。
比如,获取图片的像素点方法中:
[Objective-C] 纯文本查看 复制代码
- (uint32_t*)pixelBRGABytesFromImageRef:(CGImageRef)imageRef {    NSUInteger iWidth = CGImageGetWidth(imageRef);    NSUInteger iHeight = CGImageGetHeight(imageRef);    NSUIntegeriBytesPerPixel = 4;    NSUIntegeriBytesPerRow = iBytesPerPixel * iWidth;    NSUIntegeriBitsPerComponent = 8;    uint32_t*pixels = (uint32_t*)malloc(iWidth * iHeight *sizeof(uint32_t));     memset(pixels,0, iWidth * iHeight *sizeof(uint32_t));    CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();    CGContextRef context = CGBitmapContextCreate(pixels,                                                 iWidth,                                                 iHeight,                                                 iBitsPerComponent,                                                 iBytesPerRow,                                                 colorspace,                                                 kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);    CGRectrect =CGRectMake(0 , 0 , iWidth , iHeight);    CGContextDrawImage(context , rect ,imageRef);    CGColorSpaceRelease(colorspace);    CGContextRelease(context);    CGImageRelease(imageRef);    return pixels;}
在如上代码中的CGImageRef类型变量非OC对象,其需要手动执行释放操作CGImageRelease(imageRef)    CGColorSpaceRelease(colorspace); CGContextRelease(context),否则会造成大量的内存泄漏导致程序崩溃。其他的对于CoreFoundation框架下的某些对象或变量需要手动释放、C语言代码中的malloc等需要对应free等都需要注意。





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

使用__weak修饰的对象,引用计数不会+1,这个对象会被加入到一个weak hash表中,以对象的内存地址a(假设内存地址为a)作为key,对象作为value,存到一个hash表中。当这个对象引用计数为0的时候,会通过key为a在hash表中寻找对象,找到对象之后把对象置为nil。
发表于 2018-12-24 15:00:39 | 显示全部楼层
我只是路过打酱油的。
发表于 2018-12-24 15:33:31 | 显示全部楼层
真是难得给力的帖子啊。
发表于 2018-12-26 14:01:29 | 显示全部楼层
真是被感动的痛哭流涕……
发表于 2018-12-27 09:39:18 | 显示全部楼层
强烈支持楼主ing……
您需要登录后才可以回帖 登录 | 马上注册

本版积分规则

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

GMT+8, 2024-12-23 01:13

© 2014-2021

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