面试官比较喜欢问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对象。
viewWillAppear
和viewDidload
执行在一个runloop周期内,因此viewWillAppear方法执行的时候,mushao这个对象还存在viewDidAppear
方法执行的时候,上个runloop周期已经结束了,所以此时mushao那个对象已经释放了
其实关于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掉