内存泄漏指程序中动态分配的内存由于某种原因未释放或无法释放, 造成系统内存的浪费.
比如MRC中如下代码会造成泄漏:
NSString *string = [[NSString alloc] init];
...
// [string release]; // ARC下, 编译器自动添加此代码
但由于ARC机制, 编译器会在适当的时机帮我们加上release代码, 避免了内存泄漏. 不过即使在ARC中也有肯能因对象不释放而引起内存泄漏, 比如使用CF框架下的对象而没有做CFRelease操作.
例子中, 虽然string所占的内存很小可以忽略不计, 但不得不承认这是有安全隐患的. 假如代码中这里泄漏点内存, 那里又泄漏一点, 反反复复, 内存总会有用尽的那一刻. 毕竟系统本身内存有限, 分配给每个App的内存更加有限. 当系统内存慢慢不足时, 我们的App会变得越来越卡顿. 当系统内存告急时, App中首先会收到didReceiveMemoryWarning
提醒, 如果我们不第一时间采取措施释放内存, 那么系统就会把我们的App kill掉. 所以, 我们应该重视内存泄漏问题.
引起内存泄漏的几种原因:
@autoreleasepool
).内存泄漏和内存溢出
内存泄漏指已经废弃的内存没能被系统回收, 造成浪费.
内存溢出指App申请新内存时系统无法提供足够的内存.
内存泄漏最终会导致内存溢出, 但不一定是只有内存泄漏才会引起内存溢出, 有时候可能是因为我们操作不当引起的. 比如同时加载多个大型资源(比如图片/视频等), 或者同时做一些复杂计算(比如做一些图像处理/地图处理等), 都有可能引起内存溢出.
接下来我们讨论查找内存泄漏的几种办法.
有时候我们会收到EXC_BAD_ACCESS
错误提示, 但没能跳到具体的出错代码行, 此时可以启用Zombie Objects
功能, 来寻找那些已经被释放的对象.
当然, 这方法不一定凑效, 在我印象中没解决过什么问题.
PS: 开启Zombie Objects, Memory查看器变为disable:
测试代码:
- (void)viewDidLoad {
[super viewDidLoad];
char *bytes = CFAllocatorAllocate(CFAllocatorGetDefault(), 6, 0);
strcpy(bytes, "hello");
CFStringRef str = CFStringCreateWithCStringNoCopy(NULL, bytes, kCFStringEncodingMacRoman, NULL);
}
静态分析:
这种方法在前期检测有一定作用, 但也有可能存在误判, 这要我们点击到相应代码行去分析.
测试代码:
- (void)viewDidLoad {
[super viewDidLoad];
[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(timeIsUp) userInfo:nil repeats:YES];
}
- (void)timeIsUp {
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"111" ofType:@"png"];
UIImage *image = [[UIImage alloc] initWithContentsOfFile:filePath];
CGImageRef temImg = image.CGImage;
// 截图
temImg = CGImageCreateWithImageInRect(temImg, CGRectMake(0, 0, 100, 100));
// 释放
// CGImageRelease(temImg);
}
打开Leaks测试工具:
选择查看函数调用树:
调用列表比较复杂, 我们选择隐藏系统的一些方法:
开始运行, 无泄漏打V, 有泄漏打X:
找到出错函数, 点击可跳转到对应代码行:
https://github.com/Tencent/MLeaksFinder