AutoreleasePool创建和释放

1.为什么有了ARC还要Autorelease Pool

提到OC的RC,首先要横向对比一下Android的GC,GC的内存回收是集中式回收(定期回收),而RC的回收是伴随整个运行时的,所以android机器有种时“卡”时“流畅”的感觉,而iOS总体比较均匀,缺乏像GC的集中式回收内存的类似机制,所以猜测Pool的产生也是弥补RC的这一不足,在RC基础上进行内存优化的一种手段。

2 临时变量什么时候释放

1.如果在正常情况下,一般是超出其作用域就会立即释放

2.如果将临时变量加入了自动释放池,会延迟释放,即在runloop休眠或者autoreleasepool作用域之后释放

3.AutoreleasePool原理

1.自动释放池的本质是一个AutoreleasePoolPage结构体对象,是一个栈结构存储的页,每一个AutoreleasePoolPage都是以双向链表的形式连接

2.自动释放池的压栈和出栈主要是通过结构体的构造函数和析构函数调用底层的objc_autoreleasePoolPudh和objc_autoreleasePoolPop,实际上是调用AutoreleasePoolPage的push和pop两个方法

3.每次调用push操作其实就是创建一个新的AutoreleasePoolPage,而AutoreleasePoolPage的具体操作就是插入一个POOL_BOUNDARY,并返回插入POOL_BOUNDARY的内存地址。而push内部调用autoreleaseFast方法处理,主要有以下三种情况

当page存在,且不满时,调用add方法将对象添加至page的next指针处,并next递增
当page存在,且已满时,调用autoreleaseFullPage初始化一个新的page,然后调用add方法将对象添加至page栈中

当page不存在时,调用autoreleaseNoPage创建一个hotPage,然后调用add方法将对象添加至page栈中

4.当执行pop操作时,会传入一个值,这个值就是push操作的返回值,即POOL_BOUNDARY的内存地址token。所以pop内部的实现就是根据token找到哨兵对象所处的page中,然后使用 objc_release释放token之前的对象,并把next指针到正确位置

4. AutoreleasePool能否嵌套使用?

1.可以嵌套使用,其目的是可以控制应用程序的内存峰值,使其不要太高

2.可以嵌套的原因是因为自动释放池是以栈为节点,通过双向链表的形式连接的,且是和线程一一对应的

3.自动释放池的多层嵌套其实就是不停的pushs哨兵对象,在pop时,会先释放里面的,在释放外面的
哪些对象可以加入AutoreleasePool?alloc创建可以吗?

5.哪些对象可以加入AutoreleasePool?alloc创建可以吗?

1.在MRC下使用new、alloc、copy关键字生成的对象和retain了的对象需要手动释放,不会被添加到自动释放池中

2.在MRC下设置为autorelease的对象不需要手动释放,会直接进入自动释放池

3.所有autorelease的对象,在出了作用域之后,会被自动添加到最近创建的自动释放池中

4.在ARC下只需要关注引用计数,因为创建都是在主线程进行的,系统会自动为主线程创建AutoreleasePool,所以创建会自动放入自动释放池

6. AutoreleasePool的释放时机是什么时候?

App启动后,苹果在主线程RunLoop里注册了两个Observer,其回调都是_wrapRunLoopWithAutoreleasePoolHandler()。

2.第一个Observer监视的事件是Entry(即将进入 Loop),其回调内会调用 _objc_autoreleasePoolPush() 创建自动释放池。其order是-2147483647,优先级最高,保证创建释放池发生在其他所有回调之前。

3.第二个Observer监视了两个事件:BeforeWaiting(准备进入休眠) 时调用 _objc_autoreleasePoolPop()和_objc_autoreleasePoolPush()释放旧的池并创建新池;Exit(即 将退出Loop)时调用_objc_autoreleasePoolPop()来释放自动释放池。这个Observer的order是 2147483647,优先级最低,保证其释放池子发生在其他所有回调之后。

7.RunLoop和线程的关系

1.每个线程都有一个与之对应的RunLoop,所以RunLoop与线程是一一对应的,其绑定关系通过一个全局的DIctionary存储,线程为key,runloop为value。

2.线程中的RunLoop主要是用来管理线程的,当线程的RunLoop开启后,会在执行完任务后进行休眠状态,当有事件触发唤醒时,又开始工作,即有活时干活,没活就休息

3.主线程的RunLoop是默认开启的,在程序启动之后,会一直运行,不会退出

4.其他线程的RunLoop默认是不开启的,如果需要,则手动开启

8 小结

1.autoreleasepool其本质是一个结构体对象,一个自动释放池对象就是页,是栈结构存储,符合先进后出的原则即可

2.页的栈底是一个56字节大小的空占位符,一页总大小为4096字节

3.只有第一页有哨兵对象,最多存储504个对象,从第二页开始最多存储505个对象

4.autoreleasepool在加入要释放的对象时,底层调用的是objc_autoreleasePoolPush方法

5.autoreleasepool在调用析构函数释放时,内部的实现是调用objc_autoreleasePoolPop方法

6.一个自动释放池只有一个哨兵对象,且哨兵在第一页

7.第一页最多可以存504个对象,第二页开始最多存 505个

8.自动释放池是一个关于指针的栈结构

9.其中的指针是指向释放的对象或者pool_boundary哨兵(现在经常被称为边界)

10.自动释放池是一个页的结构(虚拟内存中提及过),而且这个页是一个双向链表(表示有父节点和子节点,在类中提及过,即类的继承链)

11.自动释放池和线程有关系

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享