iOS-autorelease

直接看第四特点内容即可。

一、实现原理

主要是通过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

  1. 取得非自己生成并持有的对象。如下代码,虽然可以使用alloc/new/copy/mutableCopy以外的方法来取得对象,但该对象已被注册到autoreleasepool。
id obj = [NSMutableArray array];

包括:

+ (id)array
{
    id obj = [[NSMutableArray alloc] init];
    return obj;
}

因为该对象作为函数的返回值,编译器会自动将其注册到autoreleasepool。

  1. 在访问附有__weak修饰符的变量时,实际上必定要访问到注册到autoreleasepool的对象。
id __weak ojb1 = obj0;
NSLog(@"%@",obj1);

实际相当于:

id __weak obj1 = obj0;
id __autorelasing tmp = obj1;
NSLog(@"%@",tmp);
  1. 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中的创建

    下面我们来解释一下这张图:
    从程序启动到加载完成是一个完整的运行循环,然后会进入休眠,等待接收用户交互,用户的每一次交互都会再次启动运行循环,来处理用户的所有的触摸、点击事件。运行循环再次启动的时候会创建一个AutoreleasePool,来处理我们编写的代码处理事件,这些都执行完了的时候,RunLoop也就会结束,RunLoop即将结束时,会销毁AutoreleasePool。

  • 其他线程
    GCD机制中的线程,这些线程默认都有自动释放池。
    NSOperation,自定义的NSOperation类中的main方法中,需要手动创建AutoreleasePool,而blockOperationinvocationOperation不需要手动创建,系统已经帮我们封装好了。
    自定义的NSThread 需要手动创建自动释放池。

2. 手动创建的场景

什么时候需要手动创建呢?

  1. 循环中创建了大量的autorelease对象,利用@autoreleasepool优化循环, 降低内存峰值。
  2. 如果你的应用程序或者线程是要长期运行的并且有可能产生大量autoreleased对象, 你应该使用autorelease pool blocks。
  3. 长期在后台中运行的任务。

四、特点

  • 以栈为节点通过双向链表的形式组合而成
  • 与线程一一对应



    对应的push、pop是通过AutoreleasePoolPage的push和pop操作来完成。


    AutoreleasePoolPage

    其中,next指向当前栈可填充的位置。
    AutoreleasePoolPage::Push

当执行push操作时,会在next的位置置为nil(哨兵对象),然后next向下移动,对象依次加入到next的位置;

AutoreleasePoolPage::Pop

参考资料:
最后的使用场景参考了iOS中autorelease的那些事儿

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容