发布于 2016-05-05 02:27:35 | 258 次阅读 | 评论: 0 | 来源: 分享
iOS苹果移动操作系统
苹果iOS是由苹果公司开发的移动操作系统。苹果公司最早于2007年1月9日的Macworld大会上公布这个系统,最初是设计给iPhone使用的,后来陆续套用到iPod touch、iPad以及Apple TV等产品上。
在MRC下, 我们需要手动管理内存, 写一大堆的retain, release代码, 稍不留神就会造成内存泄露; 而ARC下, 编译器帮我们屏蔽掉了这些繁琐的代码, 我们不需要再一条一条地写retain, release了, 可以专心地把精力放在业务逻辑, 技术上.
在MRC下, 调用[object autorelease]可以延迟对象的内存释放; 在ARC下, 我们甚至可以不需要知道 autorelease 是什么都能管理好内存. 编译器帮我们做了什么事情? 到底 autorelease 有什么神奇的地方? autorelease pool 又是个什么东西?下面我将会一一道来.
NSAutoreleasePool 是 Cocoa 用来支持引用计数内存管理机制的类, 当一个autorelease pool(自动释放池)被drain(销毁)的时候会对pool里的对象发送一条release的消息.
注意 : 在ARC下, 不能使用NSAutoreleasePool这个类来创建自动释放池, 而应该用@autoreleasepool { } 这个block, 官方文档源码如下 :
NSAutoreleasePool pool = [[NSAutoreleasePool alloc] init];
// Code benefitting from a local autorelease pool.
[pool release];
用以下代码代替上述代码 :
@autoreleasepool {
// Code benefitting from a local autorelease pool.
}
Ps : 官方文档说明, 使用@autoreleasepool这个block比NSAutoreleasePool更高效!并且在MRC环境下同样适用
让我们用一张图片来了解对象调用autorelease的整个过程
补充几点 :
1. 一个对象可以反复调用autorelease方法进入到同一个池中, 相应地, 当池子被销毁时, 该对象将会调用相同次数的release方法, 并不会造成内存泄露. 但并不建议这么做.
2. 程序中至少存在一个自动释放池, 否则autoreleased对象将不能对应收到release消息而导致内存泄露.
3. NSAutoreleasePool对象不能retain, 不能autorelease, 所以drain方法(或者release方法, 但是这两者有所不同, 下文会说)可以直接释放内存. 你应该在同一个上下文(调用创建这个池的同一个方法, 函数或者循环体)中drain一个自动释放池.
4. MRC下需要对象调用autorelease才会入池, ARC下则不需要
5. 不一定要自己创建自动释放池, 但是有3种情况下是很必要的, 下面会讲.
6. 自动释放池可以嵌套使用
下面讲autorelease pool 与 线程, RunLoop的关系
每一个线程(包括主线程)都有一个NSAutoreleasePool栈. 当一个新的池子被创建的时候, push进栈. 当池子被释放内存时, pop出栈. 对象调用autorelease方法进入栈顶的池子中. 当线程结束的时候, 它会自动地销毁掉所有跟它有关联的池子.
如果你的应用或者线程是长期存在的并且有可能产生大量的autoreleased对象, 你应该定期地drain和create自动释放池, 否则, autorelease对象会在内存中堆积造成内存告急. 这里借用土土哥的一张图,
测试的内容:500000次循环,每次循环创建一个NSNumber实例和两个NSString实例。图:红线表示没有用@autoreleasepool时的内存占用。图:绿线表示用了@autoreleasepool优化后的内存占用!
如上图所示, 在每一次循环中, 先创建一个自动释放池, 然后循环结束的时候, 自动释放池销毁, release掉池中对象, 释放内存. 对比之下, 优劣不言而喻.
苹果也是这么做的, 数组的block遍历方法就是, 大家可以自行测试.
程序运行 -> 开启事件循环 -> 发生触摸事件 -> 创建自动释放池 -> 处理触摸事件 -> 事件对象加入自动释放池 -> 一次事件循环结束, 销毁自动释放池.
苹果官方文档说 :
The Application Kit creates an autorelease pool on the main thread at the beginning of every cycle of the event loop, and drains it at the end, thereby releasing any autoreleased objects generated while processing an event
在开始每一个事件循环之前系统会在主线程创建一个自动释放池, 并且在事件循环结束的时候把前面创建的释放池释放, 回收内存. 这里不深入讲解RunLoop, 文章后面会给出几篇RunLoop的文章, 大家可以去看看.
这里介绍4个方法
- release
- drain
- autorelease
- retain
这里把他们放在一块讲, 是因为他们在引用计数环境下都能销毁一个自动释放池, 为什么这里要特意说明引用计数环境, 因为在引用计数环境和垃圾回收(GC)环境下, 这两个方法不尽相同
在引用计数环境下, release 和 drain 效果相同, 均能销毁一个自动释放池.
在垃圾回收环境下, drain同上, release 则是一个空方法.
所以建议为了兼容性, 统一用drain吧.
抛出异常, 因为NSAutoreleasePool不能调用以上 autorelease 和 retain 方法.
由于@autoreleasepool同时兼容MRC和ARC编译环境(NSAutoreleasePool只能在MRC下使用), 所以以下均是以autorelease pool block来介绍使用.
Cocoa 希望代码总是在autorelease pool block中被执行, 否则autoreleased对象就得不到释放从而造成内存泄露.
看苹果官方文档怎么说明 :
- If you are writing a program that is not based on a UI framework, such as a command-line tool.
- If you write a loop that creates many temporary objects.
You may use an autorelease pool block inside the loop to dispose of those objects before the next iteration. Using an autorelease pool block in the loop helps to reduce the maximum memory footprint of the application.- If you spawn a secondary thread.
You must create your own autorelease pool block as soon as the thread begins executing; otherwise, your application will leak objects.
@autoreleasepool { // Code here }
先上苹果官方源码– (id)findMatchingObject:(id)anObject { id match; while (match == nil) { @autoreleasepool { // Do a search that creates a lot of temporary objects. match = [self expensiveSearchForObject:anObject]; if (match != nil) { [match retain]; / Keep match around. / } } } return [match autorelease]; / Let match go and return it. /}
在block结束之后, 你要注意的是任何autoreleased对象已经被处理过了(release). 请不要对这个对象发送消息或者把这个对象当做方法的返回值返回. 会引发野指针错误.
解决方法 : 苹果是这么做的 : 在block内对match对象发送retain消息和在block外对match发送autorelease消息能延长match对象的生命周期并且允许match对象在block外部接收消息或者作为方法的返回值返回. 我们不需要再关心match什么时候释放, 因为它已经交给了上一层的autorelease pool去管理.
欢迎大家关注@Jerry4me, 同时本文有错漏的点恳请大家不吝指出, 谢谢~
参考文档 :
NSAutoreleasePool Class Reference
Using Autorelease Pool Blocks
黑幕背后的Autorelease
@autoreleasepool-内存的分配与释放
这里推荐两个RunLoop的文章
深入理解RunLoop
RunLoop