简单理解AutoreleasePool

2020-02-15  本文已影响0人  雨天多久就

面试官比较喜欢问AutoreleasePool相关的题
一般问法有两种:
1.AutoreleasePool管理的对象什么时候释放?
2.AutoreleasePool的原理是什么?
如果理解AutoreleasePool的原理,上面两个问题可以很轻松答出来

AutoreleasePool 是什么?如何存Autorelease对象?
ARC下,我们通过 @autoreleasepool{}来使用。
编译器编译后改成了如下的情况:

void *context = objc_autoreleasePoolPush();
// {}中的代码
objc_autoreleasePoolPop(context);

autoreleasePoolPush 和 autoreleasePoolPop 总是成对出现。它们是对AutoreleasePoolPage的简单封装。
AutoreleasePoolPage是一个C++类。AutoreleasePool由AutoreleasePoolPage对象通过双向指针链接组合而成。每个AutoreleasePoolPage对象会开辟4096字节内存,除了存放指针,其余空间全部用来存储Autorelease对象的地址
一个AutoreleasePoolPage存储满了之后,会新创建一个AutoreleasePoolPage对象继续存。

AutoreleasePool如何释放对象?

当进行objc_autoreleasePoolPush调用的时候,会在AutoreleasePoolPage里添加一个哨兵对象。
当objc_autoreleasePoolPop调用的时候,会将从现在位置到哨兵位置的所有对象发送release消息。
所以出来@autoreleasepool{}作用域后,Autorelease对象就会释放。

关于主线程里的Autorelease对象释放问题

主线程的Autorelease对象会自动释放。它们是在每一次runloop进行休眠的时候开始释放的。因为主线程在唤醒执行任务和休眠的时候会自动调用autoreleasePoolPush 和 autoreleasePoolPop。因此相当于整个runloop周期里产生的Autorelease对象都放在了一个自动释放池里。
下面是测试代码:
注:在arc情况下,通过__autorelease 标识可以将一个对象标识为Autorelease对象。

image.png

其实关于AutoReleasePool还有一个面试题,如下:

Question: touchesBegan方法触发后,能输出error吗?

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    NSError * error = nil;
    [self testArrayISHasCString:&error];
    NSLog(@"error:%@",error);
}

- (void)testArrayISHasCString:(NSError **)error {
    NSArray * testArray = @[@"a",@"b",@"c",@"d"];
    [testArray enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if ([obj isEqualToString:@"c"]) {
            *error = [NSError errorWithDomain:NSCocoaErrorDomain code:-1 userInfo:nil];
        }
    }];
}

答案是会crash。
因为enumerateObjectsUsingBlock会在block内部添加AutoreleasePool,所以error对象刚刚生成后,出来作用域就会释放掉。因此外界访问一个释放掉的对象,就会crash掉

上一篇下一篇

猜你喜欢

热点阅读