iOS 内存管理

自动释放池

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的循环引用的问题.


你可能感兴趣的:(iOS 内存管理)