自动释放池
AutoreleasePool的实现原理是怎么样的?
AutoreleasePool是以栈为结点,通过双向链表的形式组合而成的数据结构。编译器会将@autoreleasepool{}改写,如下 图。实际objc_autoreleasePoolPop函数在内部做了pop操作,批量将autoreleasepool中的所有的对象都会做一次release操作。
编译器改写@autoreleasepool{}
下面对上面的主要函数进行一个简单的说明
AutoreleasePool的结构
1)是以栈为结点通过双向链表的形式组合而成
2)是和线程一一对应的
[obj autorelease]的实现(对象加入自动释放池)
先判断当前next指针是否指向栈顶,如果不是直接加入;如果是,则增加一个栈结点到链表上,在新的栈添加对象;移动next指针
AutoreleasePoolPage::push实现流程(释放池多层嵌套)
插入哨兵对象
AutoreleasePoolPage::pop实现流程(与push相反)
根据传入的哨兵对象找到对应的位置
给上次push操作之后添加的对象依次添加release消息
回退next指针到正确的位置
AutoreleasePool为何可以嵌套使用?
多次插入哨兵对象,也就是对一个新的releasePool的创建,如果当前栈没有满,则不需要创建新的page,如果满了,新增一个栈节点
下面这个图中,array对象在什么时候释放呢?
在当次runloop将要结束的时候调用AutoreleasePoolPage:pop(),对array对象执行release操作。
AutoreleasePool的使用场景?
在for循环中,alloc图片数据等内存消耗较大的场景手动插入autoreleasePool,每一次for循环都进行一次内存的释放,降低内存消耗。
常见的循环引用以及破除方法:
代理(delegate)
block
NSTimer
大环引用
如何破除循环引用?
__weak
__block
__unsafe_unretained(与weak等效)
__block在ARC和MRC条件下的区别
MRC下,__block修饰对象不会增加其引用计数,避免了循环引用
ARC下,__block修饰对象会被强引用,无法避免,需手动破环
NSTimer 循环引用解决
如果没有其他处理,只是单纯地在dealloc中写定时器的销毁方法,在退出当前控制器后,由于定时器的循环引用问题导致当前类没有释放销毁,也就不会走dealloc 方法,所以退出控制器后定时器仍然在执行。
1)在退出界面时手动调用定时器销毁的方法。
2)引入中间者
3) 高级中间者
此时我们需要借助一个虚基类NSProxy,(NSProxy其主要用来消息转发的处理)
4、带block的timer
在我们创建timer的时候,苹果也意识到NSTimer的api是存在一定问题的,所以在iOS10.0之后提供了一种block的方法来去解决NSTimer的循环引用的问题.
网友评论