本系列博客是本人的源碼閱讀筆記,如果有 iOS 開發(fā)者在看 runtime 的,歡迎大家多多交流孝扛。為了方便討論,本人新建了一個微信群(iOS技術(shù)討論群)岭洲,想要加入的,請?zhí)砑颖救宋⑿牛簔hujinhui207407雁竞,【加我前請備注:ios 】钦椭,本人博客http://www.kyson.cn 也在不停的更新中,歡迎一起討論
本文完整版詳見筆者小專欄:https://xiaozhuanlan.com/runtime
背景
static_init()方法我們已經(jīng)談?wù)摰暮芏嗔吮摺=裉焓亲詈笠黄v解static_init()文章彪腔,做一個補充。我們再回顧一下方法static_init():
static void static_init()
{
size_t count;
Initializer *inits = getLibobjcInitializers(&_mh_dylib_header, &count);
for (size_t i = 0; i < count; i++) {
inits[i]();
}
}
其中进栽,_mh_dylib_header
之前的類型是mach_header_64
之前也有講解過德挣。不過這里不需要再去賦值,因為它是個extern對象(全局對象)快毛。
接下來著重解決讀者可能存在的幾個問題:
- Initializer是什么
- initsi; 這么寫的依據(jù)(含義)
分析
Initializer的聲明如下:
using Initializer = void(*)(void);
這里 using的用法稍加介紹格嗅,主要有三個作用:
- 命名空間的使用
常見的如:
using namespace std;
- 在子類中引用基類的成員
class Person {
public:
Person() :value(55) {}
virtual ~Person() {}
void test1() { cout << "Person test1..." << endl; }
protected:
int value;
};
class Man : private Person {
public:
//using Main::test1;
//using Main::value;
void test2() { cout << "value is " << value << endl; }
基類中成員變量value是protected,在private繼承之后唠帝,對于外界這個值為private屯掖,也就是說Person的對象無法使用這個value。
- 別名指定
即是本例中的用法襟衰,這個讓我們想起了typedef贴铜。那么using 跟typedef有什么區(qū)別呢?哪個更好用些呢瀑晒?這里不展開講了绍坝,有興趣的讀者可以參考這篇文章
繼續(xù),我們還是看Initializer 定義:
using Initializer = void(*)(void);
里的void(*)(void);作用苔悦。要說明這個問題轩褐,我們可以一步步分析:
void f(void)
這個不用多介紹,聲明一個返回類型為空玖详,參數(shù)為空的函數(shù) f把介;
那么
void (*p)(void)
就是聲明一個指針p勤讽,該指針指向一個返回類型為空,參數(shù)為空的函數(shù)拗踢。
最后地技,繼續(xù):
(void (*)(void))
就是表明這個指針是個指向一個返回類型為空,參數(shù)為空的函數(shù)秒拔。這下大家應(yīng)該了解了,
inits[i]();
的含義就是調(diào)用其中的函數(shù)飒硅。而且我們知道調(diào)用的函數(shù)都是類似全局變量的函數(shù)砂缩。這個之前的文章筆者有說過這里不細說了。筆者也提過這里的方法其實都是section為__mod_init_func
中的數(shù)據(jù)三娩。而通過我們之前的分析也知道庵芭,__mod_init_func
中的函數(shù)是先于main函數(shù)執(zhí)行的。其實在runtime庫中雀监,甚至咸魚load方法双吆,哈哈哈哈哈哈。
那這就給我們優(yōu)化App啟動提供了一個思路会前。幸好這個想法不是筆者特有好乐,有位iOS的同僚已經(jīng)做了相關(guān)工作,這里筆者就大概介紹一下他的思路瓦宜。
原文在這里:
一種 hook C++ static initializers 的方法
其想法很簡單蔚万,就是在load方法中hook__mod_init_func
的方法。部分源代碼如下:
+ (void)load{
sInitInfos = [NSMutableArray new];
g_initializer = new std::vector<MemoryType>();
g_cur_index = -1;
g_aslr = 0;
hookModInitFunc();
}
以及
static void hookModInitFunc(){
Dl_info info;
dladdr((const void *)hookModInitFunc, &info);
#ifndef __LP64__
// const struct mach_header *mhp = _dyld_get_image_header(0); // both works as below line
const struct mach_header *mhp = (struct mach_header*)info.dli_fbase;
unsigned long size = 0;
MemoryType *memory = (uint32_t*)getsectiondata(mhp, "__DATA", "__mod_init_func", & size);
#else /* defined(__LP64__) */
const struct mach_header_64 *mhp = (struct mach_header_64*)info.dli_fbase;
unsigned long size = 0;
MemoryType *memory = (uint64_t*)getsectiondata(mhp, "__DATA", "__mod_init_func", & size);
#endif /* defined(__LP64__) */
for(int idx = 0; idx < size/sizeof(void*); ++idx){
MemoryType original_ptr = memory[idx];
g_initializer->push_back(original_ptr);
memory[idx] = (MemoryType)myInitFunc_Initializer;
}
NSLog(@"zero mod init func : size = %@",@(size));
[sInitInfos addObject:[NSString stringWithFormat:@"ASLR=%p",mhp]];
g_aslr = (MemoryType)mhp;
}
其實大部分代碼我們都已經(jīng)可以看懂了临庇,還有疑問的讀者可以自行查閱反璃。通過這個實用工具,筆者可以輕松抓出App中啟動項的優(yōu)化空間:
如圖所示假夺,查看func函數(shù)淮蜈,可以輕松知道其所在的文件是main.mm中。然后進入筆者的main.mm函數(shù)中發(fā)現(xiàn)聲明了一個帶構(gòu)造函數(shù)的類已卷,并引用了該類:
于是進行優(yōu)化即可梧田。
參考
C++ 中using 的使用
https://stackoverflow.com/questions/20357106/what-does-c-expression-voidvoid0-mean
廣告
我的首款個人開發(fā)的APP壁紙寶貝上線了,歡迎大家下載悼尾。