直接看第四特点内容即可。
一、实现原理
主要是通过
objc_autoreleasepoolpush、objc_autoreleasepoolpop、objc_autorelease
这三个函数实现的。这三个函数其实是通过AutoreleasePoolPage 的push、pop、autorelease函数来实现的。每个AutoreleasepoolPage 实例对象的大小都是 4096 个字节。
- push,生成AutoreleasePoolPage实例。
- autorelease,相当于NSAutoreleasePool的addObject类方法,取得正在使用的poolPage实例,然后执行autoreleasePoolPage->add(obj)方法,我们可以理解为添加到内部数组中。
- pop,销毁autoreleasePoolPage实例,并调用内部数组中所有对象的release方法。
自动释放池机制就像“栈”(stack)一样。系统创建好自动释放池之后,就将其通过推入栈中;在对象上执行自动释放操作,就相当于将其放入栈顶的那个池子当中;而清空自动释放池,则相当于将其从栈中弹出。
Autoreleasepool 是一个由 AutoreleasepoolPage 双向链表的结构,其中 child 指向它的子 page,parent 指向它的父 page:
每个线程都有个AutoreleasePool栈,
二、ARC下的使用情况
1.@autoreleasepool{}来代替“NSAutoreleasePool类对象生成、持有以及废弃”。
2.__autoreleasing修饰符来代替“调用对象的autorelease方法”。
显式的附加__autoreleasing修饰符同显式的附加__strong修饰符一样罕见。
在arc中哪些地方隐式的用到了autorelease
- 取得非自己生成并持有的对象。如下代码,虽然可以使用alloc/new/copy/mutableCopy以外的方法来取得对象,但该对象已被注册到autoreleasepool。
id obj = [NSMutableArray array];
包括:
+ (id)array
{
id obj = [[NSMutableArray alloc] init];
return obj;
}
因为该对象作为函数的返回值,编译器会自动将其注册到autoreleasepool。
- 在访问附有__weak修饰符的变量时,实际上必定要访问到注册到autoreleasepool的对象。
id __weak ojb1 = obj0;
NSLog(@"%@",obj1);
实际相当于:
id __weak obj1 = obj0;
id __autorelasing tmp = obj1;
NSLog(@"%@",tmp);
- id的指针或对象的指针在没有显式指定时会被附加上__autoreleasing修饰符。
id *obj 和NSString **obj
实际上等同于
id __autoreleasing *obj 和NSString *__autoreleasing *obj
看如下的例子,比较好理解了:
//.h
- (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error
//.m
NSError *error = nil;
[[NSFileManager defaultManager] removeItemAtPath:filePath error:&error];
解释:其实它和第一种情况一样,通过alloc/new/copy/mutableCopy方法以外取得的为:非自己生成并持有的对象,(之前为返回值,现在为参数都一样),因此也会注册到autoreleasepool中。
注:下边代码会报错
NSError *error = nil;
NSError **pError = &error;
因为,赋值给对象指针时,所有权符必须一致,改为如下即可:
NSError *__strong*pError = &error;
三、AutoreleasePool的创建
1. 线程中的AutoreleasePool
-
主线程中,伴随每一个RunLoop的启动都会有默认的AutoreleasePool创建。
下面我们来解释一下这张图:
从程序启动到加载完成是一个完整的运行循环,然后会进入休眠,等待接收用户交互,用户的每一次交互都会再次启动运行循环,来处理用户的所有的触摸、点击事件。运行循环再次启动的时候会创建一个AutoreleasePool,来处理我们编写的代码处理事件,这些都执行完了的时候,RunLoop也就会结束,RunLoop即将结束时,会销毁AutoreleasePool。 - 其他线程
GCD机制中的线程,这些线程默认都有自动释放池。
NSOperation,自定义的NSOperation类中的main方法中,需要手动创建AutoreleasePool,而blockOperation和invocationOperation则不需要手动创建,系统已经帮我们封装好了。
自定义的NSThread 需要手动创建自动释放池。
2. 手动创建的场景
什么时候需要手动创建呢?
- 循环中创建了大量的autorelease对象,利用@autoreleasepool优化循环, 降低内存峰值。
- 如果你的应用程序或者线程是要长期运行的并且有可能产生大量autoreleased对象, 你应该使用autorelease pool blocks。
- 长期在后台中运行的任务。
四、特点
- 以栈为节点通过双向链表的形式组合而成
-
与线程一一对应
对应的push、pop是通过AutoreleasePoolPage的push和pop操作来完成。
其中,next指向当前栈可填充的位置。
当执行push操作时,会在next的位置置为nil(哨兵对象),然后next向下移动,对象依次加入到next的位置;
参考资料:
最后的使用场景参考了iOS中autorelease的那些事儿