面试题答案
一键面试autoreleasepool在多线程中的行为变化
- 独立管理:每个线程都有自己独立的自动释放池栈。主线程有一个隐含的自动释放池,而子线程没有,需要手动创建和管理。这意味着在不同线程中发送
autorelease
消息的对象,会在各自线程的自动释放池被销毁时释放内存,互不干扰。 - 生命周期:主线程的自动释放池通常在每次事件循环结束时被排空和重新创建;而子线程的自动释放池需要根据具体代码逻辑,在合适的时机创建和销毁。如果没有及时创建自动释放池,在子线程中频繁发送
autorelease
消息会导致对象不断积累,直到线程结束才释放,可能引发内存峰值过高的问题。
多线程环境下autoreleasepool的优化
- 及时创建:在子线程开始执行任务前,尽快创建自动释放池。例如在
NSThread
的main
方法或GCD
的任务块中,一开始就创建自动释放池。 - 合理嵌套:对于复杂任务,如果存在多个逻辑块,每个逻辑块可以创建自己的子自动释放池。这样在一个逻辑块执行完毕后,相关的自动释放对象可以及时释放,减少内存占用。
- 手动释放:对于一些创建和释放频繁且占用内存较大的对象,可以考虑手动管理内存,而不是依赖
autorelease
。
示例
- 使用GCD
dispatch_queue_t queue = dispatch_queue_create("com.example.thread", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
@autoreleasepool {
for (int i = 0; i < 100000; i++) {
NSString *str = [[NSString alloc] initWithFormat:@"%d", i];
// 处理str
}
}
});
在上述代码中,通过dispatch_async
开启一个异步任务,在任务块中创建了自动释放池。这样在循环中创建的大量NSString
对象,会在每次循环结束后(自动释放池销毁时)及时释放内存,避免内存峰值过高。
2. 使用NSThread
[NSThread detachNewThreadSelector:@selector(subThreadTask) toTarget:self withObject:nil];
- (void)subThreadTask {
@autoreleasepool {
for (int i = 0; i < 100000; i++) {
NSObject *obj = [[NSObject alloc] init];
// 处理obj
}
}
}
这里通过detachNewThreadSelector
开启新线程,在新线程的subThreadTask
方法中创建自动释放池,同样能保证循环中创建的NSObject
对象及时释放内存。