一、问题
1. autorelease何时释放
App启动后,苹果在主线程 RunLoop 里注册了两个 Observer,其回调都是 RunLoopWithAutoreleasePoolHandler()
第一个 Observer : 监视的事件是 Entry (即将进入Loop),其回调内会调用 _objc_autoreleasePoolPush() 创建自动释放池。
第二个 Observer : 监视了两个事件:BeforeWaiting(准备进入休眠) 会调用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 释放旧的池并创建新池;
Exit(即将退出Loop) 会调用 _objc_autoreleasePoolPop( ) 来释放自动释放池。
手动干预释放时机
- 指定autoreleasepool 就是所谓的:当前作用域大括号结束时释放。
系统自动去释放
- 不手动指定 autoreleasepool,依赖于 main 函数所包的 autoreleasepool, Autorelease对象出了作用域之后,会被添加到最近一次创建的自动释放池中,并会在当前的 runloop 迭代结束时释放。
2. 子线程是否要手动创建autoreleasepool
NSThread 和 NSOperationQueue
- 开辟子线程需要手动创建 autoreleasepool。
GCD
- 开辟子线程不需要手动创建 autoreleasepool,因为 GCD的每个队列都会自行创建 autoreleasepool。
3. autorelease 对象在什么情况下会被释放?
分两种情况,手动干预释放 和 系统自动释放。
手动干预释放:
- 就是指定 autoreleasepool, 当前作用域大括号结束就立即释放。
系统自动去释放:
- 不手动指定 autoreleasepool, Autorelease对象出了作用域之后,会被添加到最近一次创建的自动释放池中,并会在当前的 runloop 迭代结束时释放。
kCFRunLoopEntry(1):
第一次进入会自动创建一个autorelease
kCFRunLoopBeforeWaiting(32):
进入休眠状态前会自动销毁一个autorelease,然后重新创建一个新的autorelease
kCFRunLoopExit(128):
退出runloop时会自动销毁最后一个创建的autorelease
4. runloop的mode作用是什么?
model 主要是用来指定事件在运行循环中的优先级的,分为:
- NSDefaultRunLoopMode(kCFRunLoopDefaultMode):默认,空闲状态
- UITrackingRunLoopMode:ScrollView滑动时
- UIInitializationRunLoopMode:启动时
- NSRunLoopCommonModes(kCFRunLoopCommonModes):Mode集合
苹果公开提供的 Mode 有两个:
- NSDefaultRunLoopMode(kCFRunLoopDefaultMode)
- NSRunLoopCommonModes(kCFRunLoopCommonModes)
二、AutoreleasePool
1. 什么是 AutoreleasePool
AutoreleasePool(自动释放池)是OC中的一种内存自动回收机制,我们可以给一个对象发送 autorelease 消息,然后当一个runloop 迭代结束时系统才会一次性清理掉被autorelease处理过的对象。
AutoreleasePool 一般用在比如 编写的循环创建了很多临时对象。[UIimage imageWithName:@“”] 避免内存峰值过高。
2. AutoreleasePool 底层结构
AutoreleasePool 底层结构
- 主要包含 AutoreleasePool 和 AutoreleasePoolPage, 调用 autorelease 的对象 最终都是通过 AutoreleasePoolPage 进行管理。
**@AutoreleasePool **
- 会自动转换为 __AtAutoreleasePool 结构体类型,在代码的前后会调用 objc_autoreleasePoolPush 和 objc_autoreleasePoolPop 方法.
AutoreleasePool
- 内部是个 双向链表 的结构,每一个都是一个 AutoreleasePoolPage 进行相连,以 child 和 parent 指针进行相连。
push 与 pop 的具体做了什么
objc_autoreleasePoolPush
- objc_autoreleasePoolPush 会将一个 pool_boundary 压入 poolpage 里面,然后调用 autorelease 的时候,会跟着 pool_boundary 一个一个放入 poolpage 里面。
objc_autoreleasePoolPop
- objc_autoreleasePoolPop 的 时候会把 边界哨兵对象传递进去,然后从最后一个压入一个 poolpage 的对象,一直调用 release,直到传入的 pool_boundary 哨兵对象。
Runloop 和 Autorelease 释放时机
App启动后,苹果在主线程 RunLoop 里注册了两个 Observer,其回调都是 RunLoopWithAutoreleasePoolHandler()
第一个 Observer :
- 监视的事件是 Entry (即将进入Loop),其回调内会调用 _objc_autoreleasePoolPush() 创建自动释放池。
第二个 Observer :
- 监视了两个事件:BeforeWaiting(准备进入休眠) 会调用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 释放旧的池并创建新池;
Exit(即将退出Loop)
- 会调用 _objc_autoreleasePoolPop( ) 来释放自动释放池。
三、@autoreleasepool 底层
-
自动释放池 @autoreleasepool 被转换成 __AtAutoreleasePool 结构体类型。
-
实质上是一个__AtAutoreleasePool 的结构体对象。
-
只是帮助我们少写了这两行代码而已,然后要根据上述两个方法来分析自动释放池的实现。
int main(int argc, char * argv[]) {
/* @autoreleasepool */ {
// 创建自动释放池
__AtAutoreleasePool __autoreleasepool = objc_autoreleasePoolPush();
// TODO 执行各种操作,将对象加入自动释放池
// 释放自动释放池
objc_autoreleasePoolPop(__autoreleasepool)
}
}
__AtAutoreleasePool 结构体内容
struct __AtAutoreleasePool {
__AtAutoreleasePool() {
// objc_autoreleasePoolPush 会调用 AutoreleasePoolPage 的 push 方法
atautoreleasepoolobj = objc_autoreleasePoolPush();
}
~__AtAutoreleasePool() {
// objc_autoreleasePoolPop 会调用 AutoreleasePoolPage 的 pop 方法
objc_autoreleasePoolPop(atautoreleasepoolobj);
}
};
底层调用
void *objc_autoreleasePoolPush(void)
{
return AutoreleasePoolPage::push();
}
void objc_autoreleasePoolPop(void *ctxt)
{
AutoreleasePoolPage::pop(ctxt);
}
AutoreleasePoolPage
POOL_BOUNDARY 边界对象
define POOL_BOUNDARY nil
objc_autoreleasePoolPush 会把一个哨兵对象放到 POOL——BOUNDARY 里面。 之前的源代码变量名是 POOL_BOUNDARY 边界对象,用来区别每个 page 即每个 AutoreleasePoolPage 边界。