iOS开发之runtime(15):static_init()提升启动速度

logo

本系列博客是本人的源码阅读笔记,如果有 iOS 开发者在看 runtime 的,欢迎大家多多交流。为了方便讨论,本人新建了一个微信群(iOS技术讨论群),想要加入的,请添加本人微信:zhujinhui207407,【加我前请备注:ios 】,本人博客http://www.kyson.cn 也在不停的更新中,欢迎一起讨论

本文完整版详见笔者小专栏:https://xiaozhuanlan.com/runtime

背景

static_init()方法我们已经谈论的很多了。今天是最后一篇讲解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; 这么写的依据(含义)

分析

Initializer的声明如下:

using Initializer = void(*)(void);

这里 using的用法稍加介绍,主要有三个作用:

  1. 命名空间的使用
    常见的如:
using namespace std;
  1. 在子类中引用基类的成员
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。

  1. 别名指定
    即是本例中的用法,这个让我们想起了typedef。那么using 跟typedef有什么区别呢?哪个更好用些呢?这里不展开讲了,有兴趣的读者可以参考这篇文章

继续,我们还是看Initializer 定义:

using Initializer = void(*)(void);

里的void(*)(void);作用。要说明这个问题,我们可以一步步分析:

void f(void)

这个不用多介绍,声明一个返回类型为空,参数为空的函数 f;
那么

void (*p)(void)

就是声明一个指针p,该指针指向一个返回类型为空,参数为空的函数。
最后,继续:

(void (*)(void))

就是表明这个指针是个指向一个返回类型为空,参数为空的函数。这下大家应该了解了,

inits[i]();

的含义就是调用其中的函数。而且我们知道调用的函数都是类似全局变量的函数。这个之前的文章笔者有说过这里不细说了。笔者也提过这里的方法其实都是section为__mod_init_func中的数据。而通过我们之前的分析也知道,__mod_init_func中的函数是先于main函数执行的。其实在runtime库中,甚至咸鱼load方法,哈哈哈哈哈哈。
那这就给我们优化App启动提供了一个思路。幸好这个想法不是笔者特有,有位iOS的同僚已经做了相关工作,这里笔者就大概介绍一下他的思路。

原文在这里:
一种 hook C++ static initializers 的方法
其想法很简单,就是在load方法中hook__mod_init_func的方法。部分源代码如下:

+ (void)load{
    sInitInfos = [NSMutableArray new];
    g_initializer = new std::vector();
    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;
}

其实大部分代码我们都已经可以看懂了,还有疑问的读者可以自行查阅。通过这个实用工具,笔者可以轻松抓出App中启动项的优化空间:


优化空间一览

如图所示,查看func函数,可以轻松知道其所在的文件是main.mm中。然后进入笔者的main.mm函数中发现声明了一个带构造函数的类,并引用了该类:


追踪

于是进行优化即可。

参考

C++ 中using 的使用
https://stackoverflow.com/questions/20357106/what-does-c-expression-voidvoid0-mean

广告

我的首款个人开发的APP壁纸宝贝上线了,欢迎大家下载。

壁纸宝贝

你可能感兴趣的:(iOS开发之runtime(15):static_init()提升启动速度)