搞懂Objective-C中的autorelease
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了搞懂Objective-C中的autorelease相关的知识,希望对你有一定的参考价值。
参考技术A本文是上一篇: 搞懂Objective-C中的ARC 的延伸和补充
操作系统实现了一个线程工厂称为线程池,还有一个配合工作的管理器称为调度中心,ios系统并没有接口去直接操作线程池,线程的创建、调度、销毁全权交给线程池,当我们试图去创建一个线程的时候,调度中心会 检查线程池的运行状态、运行线程数、运行策略,决定接下来的执行流程,是直接申请线程执行还是放入缓冲队列中,待到执行的时候,线程池决定是开辟新的线程还是复用现有空闲线程,长时间空闲的线程会被回收 (一般10s左右)
iOS操作系统为 线程运行 配备了一套数据结构, 一个栈、一个autoreleasePool、一个runloop(懒加载) 以及一些用于控制状态的标志位变量
好的~我们回到问题本身
先说结论:
再看个demo,这里只讨论非主线程:
如上创建了一个串行队列,写了个死循环,一直在创建堆区字符串,但是内存不会涨一丝一毫,原因上一篇文章 搞懂Objective-C中的ARC 讲过,这个对象直接会被release,不会加入autoreleasePool
修改一下:
会发现非主线程创建autorelease对象会导致内存无休止的在涨,直到OOM
再次修改:
结果是内存也不会涨一丝一毫,数据刚好能对应上面的结论,对于有知其所以然需求的可以往下看
前辈 这篇 讲得很好了,本文会基于最新源码做个浅析(可能不是最新源码,作者开始看源码时候的版本objc4-781.2),顺便讲述下如何查阅源码, 源码链接
会发现AutoreleasePoolPage是一个c++的类,继承AutoreleasePoolPageData,具体实现很长,这里省去部分函数实现
还是很长是不是,但是对于理解原理都重要,看看苹果自己的注释
/***********************************************************************
Autorelease pool implementation
A thread\'s autorelease pool is a stack of pointers.
Each pointer is either an object to release, or POOL_BOUNDARY which is
an autorelease pool boundary.
A pool token is a pointer to the POOL_BOUNDARY for that pool. When
the pool is popped, every object hotter than the sentinel is released.
The stack is divided into a doubly-linked list of pages. Pages are added
and deleted as necessary.
Thread-local storage points to the hot page, where newly autoreleased
objects are stored.
**********************************************************************/
/***********************************************************************
Autorelease pool 的实现
线程的Autorelease pool是一系列声明在栈上的指针。
每个指针要么是一个需要释放的对象,要么是 POOL_BOUNDARY,它是
Autorelease pool边界。
token是指向该池的 POOL_BOUNDARY 的指针。 什么时候
池被弹出,每个比哨兵更热的对象都被释放。
栈被分成一个双向链接的AutoreleasePoolPage列表。 Pages根据需要,添加
和删除。
线程本地存储指向最新被存储到自动释池所在的热点页面
。
**********************************************************************/
看完这个注释,我觉得都不用往下讲了😂,一个字,清晰透彻
next指针作为游标指向栈顶最新add进来的autorelease对象的下一个位置,当有新的obj加入autoreleasePool的时候取到next指针的地址存储obj,next指针向栈顶方向移动8个字节
每个autoreleasePool初始化的时候都会绑定当前线程,如下是AutoreleasePoolPage的初始化函数
第一个参数是AutoreleasePoolPage的标识,第二个参数是一个函数指针,线程退出之前会执行这个函数,就是tls_dealloc,调用栈如下:
线程休眠之前,runloop会调用objc_autoreleasePoolPop,上面流程除了第一步,后面一样
单向的可以吗?可以呀,但是查询速度会以指数级别降低,删除栈顶page后想删除的倒数第二个page,怎么办,从头开始遍历呗,遍历n-1次,然后遍历n-2次.......
所以为什么设计成双向链表,效率高,如此而已
一文搞懂│php 中的 DI 依赖注入
以上是关于搞懂Objective-C中的autorelease的主要内容,如果未能解决你的问题,请参考以下文章