Initializers是指在main函数之前执行的初始化代码,包括C++静态构造函数和__attribute__((constructor))标记的函数。这些代码会在Pre-main阶段同步执行,阻塞启动。
问题分析
以下代码会在main之前执行:
1. C++静态构造函数
__attribute__((constructor))
static void MyInitializer() {
// 耗时初始化
initializeHeavyResource();
}
2. 全局静态变量的构造函数
// 全局静态变量
static std::vector<std::string> globalCache = loadFromDisk(); // 构造函数在main前调用
3. 非基本类型的全局变量
// 非POD类型的全局变量会触发构造函数
static std::string globalString = "Hello";
static MyClass globalObject;
优化方案
方案1:延迟初始化
将全局变量改为懒加载模式:
// 优化前:全局变量在main前初始化
static std::vector<std::string> globalCache = loadFromDisk();
// 优化后(纯C++方案,适用于 .cpp 文件):
#include <mutex>
static std::vector<std::string>* getGlobalCache() {
static std::vector<std::string>* cache = nullptr;
static std::once_flag onceFlag;
std::call_once(onceFlag, [&] {
cache = new std::vector<std::string>();
*cache = loadFromDisk();
});
return cache;
}
// 优化后(ObjC++方案,适用于 .mm 文件):
static std::vector<std::string>* getGlobalCache() {
static std::vector<std::string>* cache = nullptr;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
cache = new std::vector<std::string>();
*cache = loadFromDisk();
});
return cache;
}
方案2:使用Swift懒加载
Swift的懒加载特性可以避免在启动时初始化:
class ResourceManager {
// 首次访问时才初始化
static let shared = ResourceManager()
// 懒加载属性
lazy var heavyResource: HeavyResource = {
return HeavyResource()
}()
private init() {
// 轻量级初始化
}
}
方案3:移除不必要的constructor函数
审查项目中所有__attribute__((constructor))的使用,将非必要的初始化延迟:
// 优化前:在constructor中执行耗时操作
__attribute__((constructor))
static void setupAnalytics() {
// 耗时的初始化
AnalyticsSDK::initialize();
AnalyticsSDK::setUserID(getUserID());
}
// 优化后:只做轻量级注册,延迟实际初始化
__attribute__((constructor))
static void registerAnalytics() {
// 只注册,不初始化
LaunchTaskManager::registerTask([]() {
AnalyticsSDK::initialize();
AnalyticsSDK::setUserID(getUserID());
});
}
方案4:使用基本类型替代复杂类型
对于全局变量,尽量使用基本类型:
// 优化前:使用std::string
static std::string kAPIEndpoint = "https://api.example.com";
// 优化后:使用C字符串
static const char* kAPIEndpoint = "https://api.example.com";
方案5:使用函数局部静态变量
C++11保证函数局部静态变量的线程安全初始化:
// 优化前:全局静态变量
static ExpensiveObject globalObject;
// 优化后:函数局部静态变量,首次调用时才初始化
ExpensiveObject& getGlobalObject() {
static ExpensiveObject instance; // 首次调用时初始化
return instance;
}
如何找出所有Initializers
方法1:使用otool分析
# 查看Mach-O文件中的初始化器
otool -l /path/to/YourApp.app/YourApp | grep -A5 "__mod_init_func"
# 也可直接查看 section 内容(可能位于 __DATA 或 __DATA_CONST 段)
otool -v -s __DATA __mod_init_func /path/to/YourApp.app/YourApp
otool -v -s __DATA_CONST __mod_init_func /path/to/YourApp.app/YourApp
方法2:使用Instruments
Time Profiler可以显示Pre-main阶段所有函数的调用和耗时。