0x0 Xcode 11 改版后模板 main.m 的变化
在 Xcode 11 中新建一个 OC 的项目,可以留意到模板项目中 main.m 发生了一些细微的变化。
int main(int argc, char * argv[]) {
NSString * appDelegateClassName;
@autoreleasepool {
// Setup code that might create autoreleased objects goes here.
appDelegateClassName = NSStringFromClass([AppDelegate class]);
}
return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
在这之前是这样:
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
对比可以看到是关键字 @autoreleasepool
的作用范围发生了变化。
0x1 差异分析
通过以下命令将文件进行重写为 C++
,可以进一步了解 @autoreleasepool 的实现
// 终端 cd 到对应文件路径下
-rewrite-objc main.m
两种方式下的代码如下:
// Xcode 11
int main(int argc, char * argv[]) {
NSString * appDelegateClassName;
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
appDelegateClassName = NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class")));
}
return UIApplicationMain(argc, argv, __null, appDelegateClassName);
}
// Xcode 10 及以下
int main(int argc, char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
return UIApplicationMain(argc, argv, __null,
NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
}
}
其中 @autoreleasepool 是一个 struct,定义如下:
extern "C" __declspec(dllimport) void * objc_autoreleasePoolPush(void);
extern "C" __declspec(dllimport) void objc_autoreleasePoolPop(void *);
struct __AtAutoreleasePool {
__AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
void * atautoreleasepoolobj;
};
根据 C++ 对象的构造和析构原理:
根据构造函数和析构函数的特点(自动局部变量的构造函数是在程序执行到声明这个对象的位置时调用的,而对应的析构函数是在程序执行到离开这个对象的作用域时调用),我们可以将上面两段代码简化成如下形式:
/* @autoreleasepool */ { void *atautoreleasepoolobj = objc_autoreleasePoolPush(); // 中间代码 objc_autoreleasePoolPop(atautoreleasepoolobj); }
0x2 结论
- 老版本虽然是将整个程序运行放置在 @autoreleasepool 范围内,但实际上要到程序执行到 return 才会触发自动释放,而在 iOS/macOS 中因为 RunLoop 的存在着几乎是不发生的,这意味着老版本的 @autoreleasepool 中声明的局部变量要到程序结束才会释放;
- 对比 Xcode 11 的处理,将 @autoreleasepool 放在触发主线程 RunLoop 的 UIApplicationMain 函数外围,可以保证此处局部变量在程序启动后立即释放。这也正是模板中注释的意图。
// Setup code that might create autoreleased objects goes here.
参考资料
- Objective-C编译成C++代码报错
- AutoreleasePool的原理和实现
加个微信,交个朋友
微博:@创联维新
微信:↓↓