ios应用启动加载过程 _objc_init()

app的启动入口在main函数,那么main之前做了什么呢?我们知道load方法加载在main函数之前,我们在load方法出加一个断点并启动。


dyld.png

从上图我们可以看到程序启动调用库函数的大概流程,这里涉及了dyldlibdispatchLibsystem,调用流程如下:

  1. dyld::start()
  2. dyld::_main()
  3. dyld::initializeMainExecutable()
  4. 初始化过程会递归调用
  5. libSystem::libSystem_initializer()
  6. libdispatch::libdispatch_init()
  7. libdispatch::_os_object_init()
  8. objc::_objc_init()

这些不是我们分析的重点,有兴趣可以参考dyld源码解读,下面我们重点分析一下objc库的_objc_init()函数

一、_objc_init()

/***********************************************************************
* _objc_init
* Bootstrap initialization. Registers our image notifier with dyld.
* Called by libSystem BEFORE library initialization time
**********************************************************************/

void _objc_init(void)
{
    static bool initialized = false;
    if (initialized) return;
    initialized = true;
    
    // fixme defer initialization until an objc-using image is found?
    environ_init();    //环境变量初始化
    tls_init();        //线程key绑定
    static_init();     //c++静态构造函数
    lock_init();
    exception_init();  //注册回调 异常处理
    
    // dyld
    _dyld_objc_notify_register(&map_images, load_images, unmap_image);
}

environ_init()函数改造一下(去除if判断,把下面代码拿出来),就可以打印所有的环境变量

    for (size_t i = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++) {
        const option_t *opt = &Settings[i];
         _objc_inform("%s: %s", opt->env, opt->help);
        if (PrintOptions && *opt->var) _objc_inform("%s is set", opt->env);
    }

_dyld_objc_notify_register:这个是dyld提供的一个方法,用于注册回调,下面是源码注释。

// Note: only for use by objc runtime
// Register handlers to be called when objc images are mapped, unmapped, and initialized.
// Dyld will call back the "mapped" function with an array of images that contain an objc-image-info section.
// Those images that are dylibs will have the ref-counts automatically bumped, so objc will no longer need to
// call dlopen() on them to keep them from being unloaded.  During the call to _dyld_objc_notify_register(),
// dyld will call the "mapped" function with already loaded objc images.  During any later dlopen() call,
// dyld will also call the "mapped" function.  Dyld will call the "init" function when dyld would be called
// initializers in that image.  This is when objc calls any +load methods in that image.
//
void _dyld_objc_notify_register(_dyld_objc_notify_mapped    mapped,
                                _dyld_objc_notify_init      init,
                                _dyld_objc_notify_unmapped  unmapped);

&map_images和load_images调用方式一样的

二、map_images

我们先来研究一下map_images,经过代码跳转,最终进入到_read_images,这个函数代码很长,我们先把它简化一下,便于我们理解阅读:

/***********************************************************************
* _read_images
* Perform initial processing of the headers in the linked 
* list beginning with headerList. 
*
* Called by: map_images_nolock
*
* Locking: runtimeLock acquired by map_images
**********************************************************************/
void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses){
    
    // 1:第一次进来 - 开始创建表
    // gdb_objc_realized_classes : 所有类的表 - 包括实现的和没有实现的
    // allocatedClasses: 包含用objc_allocateClassPair分配的所有类(和元类)的表。(已分配)
    if (!doneOnce) {
           doneOnce = YES;
        // namedClasses
        // Preoptimized classes don't go in this table.
        // 4/3 is NXMapTable's load factor
        int namedClassesSize =
            (isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
        gdb_objc_realized_classes =
            NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
        
        allocatedClasses = NXCreateHashTable(NXPtrPrototype, 0, nil);
    }
    
    // 2:类处理
    for (i = 0; i < count; i++) {
      Class cls = (Class)classlist[i];
      Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
    }
    
    // 3: 方法编号处理
    for (EACH_HEADER) {
        SEL *sels = _getObjc2SelectorRefs(hi, &count);
        UnfixedSelectors += count;
        for (i = 0; i < count; i++) {
          const char *name = sel_cname(sels[i]);
          sels[i] = sel_registerNameNoLock(name, isBundle);
        }
    }

    // 4: 协议处理
    for (EACH_HEADER) {
        extern objc_class OBJC_CLASS_$_Protocol;
        Class cls = (Class)&OBJC_CLASS_$_Protocol;
        NXMapTable *protocol_map = protocols();
        protocol_t **protolist = _getObjc2ProtocolList(hi, &count);
        for (i = 0; i < count; i++) {
            readProtocol(protolist[i], cls, protocol_map,
                         isPreoptimized, isBundle);
        }
    }
    
    // 5: 非懒加载类处理
    for (EACH_HEADER) {
      classref_t *classlist =
          _getObjc2NonlazyClassList(hi, &count);
      addClassTableEntry(cls);
      realizeClassWithoutSwift(cls);
    }
    
    // 6: 待处理的类
    if (resolvedFutureClasses) {
        for (i = 0; i < resolvedFutureClassCount; i++) {
            Class cls = resolvedFutureClasses[i];
            if (cls->isSwiftStable()) {
                _objc_fatal("Swift class is not allowed to be future");
            }
            realizeClassWithoutSwift(cls);
            cls->setInstancesRequireRawIsa(false/*inherited*/);
        }
        free(resolvedFutureClasses);
    }
    
    // 7:分类处理
   for (EACH_HEADER) {
       category_t **catlist =
           _getObjc2CategoryList(hi, &count);
       bool hasClassProperties = hi->info()->hasCategoryClassProperties();
       for (i = 0; i < count; i++) {
           category_t *cat = catlist[i];
           Class cls = remapClass(cat->cls);
       }
   }

}

2.1 doneOnce

doneOnce:即只执行一次。
initializeTaggedPointerObfuscatorTaggedPointer 初始化
gdb_objc_realized_classes : 所有类的表 - 包括实现的和没有实现的
allocatedClasses: 包含用objc_allocateClassPair分配的所有类(和元类)的表。(已分配)

2.2 类处理

for (i = 0; i < count; i++) {
     // 数组中会取出OS_dispatch_queue_concurrent、OS_xpc_object、NSRunloop等系统类,例如CF、Fundation、libdispatch中的类。以及自己创建的类
    Class cls = (Class)classlist[i];
    
    // 通过readClass函数获取处理后的新类,
    Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);

    // 初始化所有懒加载的类需要的内存空间 - 现在数据没有加载到的 - 连类都没有初始化的
    if (newCls != cls  &&  newCls) {
        // Class was moved but not deleted. Currently this occurs 
        // only when the new class resolved a future class.
        // Non-lazily realize the class below.

        // 将懒加载的类添加到数组中
        resolvedFutureClasses = (Class *)
            realloc(resolvedFutureClasses, 
                    (resolvedFutureClassCount+1) * sizeof(Class));
        resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
    }
}

readClass读取类, 并把类插入到上面提到的两个表。

Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
{
    Class replacing = nil;
    //未来需要处理的类才会处理,自己写的类不会处理,即不会进行ro、rw处理
    if (Class newCls = popFutureNamedClass(mangledName)) {
        // This name was previously allocated as a future class.
        // Copy objc_class to future class's struct.
        // Preserve future's rw data block.
        
        if (newCls->isAnySwift()) {
            _objc_fatal("Can't complete future class request for '%s' "
                        "because the real class is too big.", 
                        cls->nameForLogging());
        }
        
        class_rw_t *rw = newCls->data();
        const class_ro_t *old_ro = rw->ro;
        memcpy(newCls, cls, sizeof(objc_class));
        rw->ro = (class_ro_t *)newCls->data();
        newCls->setData(rw);
        freeIfMutable((char *)old_ro->name);
        free((void *)old_ro);
        
        addRemappedClass(cls, newCls);
        
        replacing = cls;
        cls = newCls;
    }
    
    if (headerIsPreoptimized  &&  !replacing) {

    } else {
        //把类插入总表
        addNamedClass(cls, mangledName, replacing);
        addClassTableEntry(cls);
    }
    return cls;
}

if (newCls != cls && newCls)为未来类时,会把类的地址重新赋值,进入到if语句。最终关联到下面的代码,加载懒加载类:

// Realize newly-resolved future classes, in case CF manipulates them
    // 遍历resolvedFutureClasses数组,实现懒加载的类
    if (resolvedFutureClasses) {
        for (i = 0; i < resolvedFutureClassCount; i++) {
            Class cls = resolvedFutureClasses[i];
            if (cls->isSwiftStable()) {
                _objc_fatal("Swift class is not allowed to be future");
            }
            // 实现懒加载的类
            realizeClassWithoutSwift(cls);
            cls->setInstancesRequireRawIsa(false/*inherited*/);
        }
        free(resolvedFutureClasses);
    }

2.3 SEL处理

// 将所有SEL都注册到哈希表中,是另外一张全局哈希表
// Fix up @selector references
static size_t UnfixedSelectors;
{
    mutex_locker_t lock(selLock);
    for (EACH_HEADER) {
        if (hi->isPreoptimized()) continue;
        
        bool isBundle = hi->isBundle();
        SEL *sels = _getObjc2SelectorRefs(hi, &count);
        UnfixedSelectors += count;
        for (i = 0; i < count; i++) {
            const char *name = sel_cname(sels[i]);
            // 注册SEL的操作
            sels[i] = sel_registerNameNoLock(name, isBundle);
        }
    }
}

2.4 非懒加载类

// Realize non-lazy classes (for +load methods and static instances)
// 实现非懒加载的类,对于load方法和静态实例变量
for (EACH_HEADER) {
    classref_t *classlist = 
        _getObjc2NonlazyClassList(hi, &count);
    for (i = 0; i < count; i++) {
        Class cls = remapClass(classlist[i]);
        // printf("non-lazy Class:%s\n",cls->mangledName());
        if (!cls) continue;

        addClassTableEntry(cls);

        // 实现所有非懒加载的类(实例化类对象的一些信息,例如rw)
        realizeClassWithoutSwift(cls);
    }
}

2.4.1 realizeClassWithoutSwift

递归调用初始化类以及父类,ro赋值,superclass和subclass的指向。

static Class realizeClassWithoutSwift(Class cls)
{
    ro = (const class_ro_t *)cls->data();
    if (ro->flags & RO_FUTURE) {
        
    } else {
        //ro赋值
        // Normal class. Allocate writeable class data.
        rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
        rw->ro = ro;
        rw->flags = RW_REALIZED|RW_REALIZING;
        cls->setData(rw);
    }

    //递归初始化父类rw,保证 superclass 和 isa 的完整性,也就是保证类的完整性
    // Realize superclass and metaclass, if they aren't already.
    // This needs to be done after RW_REALIZED is set above, for root classes.
    // This needs to be done after class index is chosen, for root metaclasses.
    // This assumes that none of those classes have Swift contents,
    //   or that Swift's initializers have already been called.
    //   fixme that assumption will be wrong if we add support
    //   for ObjC subclasses of Swift classes.
    supercls = realizeClassWithoutSwift(remapClass(cls->superclass));
    metacls = realizeClassWithoutSwift(remapClass(cls->ISA()));


    // Update superclass and metaclass in case of remapping
    cls->superclass = supercls;
    cls->initClassIsa(metacls);

    //不光要把父类关联到类上面,还要让父类知道子类的存在。
    // Connect this class to its superclass's subclass lists
    if (supercls) {
        addSubclass(supercls, cls);
    } else {
        addRootClass(cls);
    }

    // Attach categories
    methodizeClass(cls);

    return cls;
}

9-12行代码:通过 calloc 开辟内存空间,返回一个新的 rw,把 cls取出来的 ro 赋值给这个 rw,将 rw 设置到 cls 身上。
那么rw是不是就有值了,下面我们用LLDB看下:

lldb-rw-ro.png

2.4.2 methodizeClass

这个方法主要完成了从ro读取数据,然后赋值给rw

/***********************************************************************
* methodizeClass
* Fixes up cls's method list, protocol list, and property list.
* Attaches any outstanding categories.
* Locking: runtimeLock must be held by the caller
**********************************************************************/
static void methodizeClass(Class cls)
{
    runtimeLock.assertLocked();

    bool isMeta = cls->isMetaClass();
    auto rw = cls->data();
    auto ro = rw->ro;

    // Install methods and properties that the class implements itself.
    method_list_t *list = ro->baseMethods();
    if (list) {
        prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
        rw->methods.attachLists(&list, 1);
    }

    property_list_t *proplist = ro->baseProperties;
    if (proplist) {
        rw->properties.attachLists(&proplist, 1);
    }

    protocol_list_t *protolist = ro->baseProtocols;
    if (protolist) {
        rw->protocols.attachLists(&protolist, 1);
    }

    // Root classes get bonus method implementations if they don't have 
    // them already. These apply before category replacements.
    if (cls->isRootMetaclass()) {
        // root metaclass
        addMethod(cls, SEL_initialize, (IMP)&objc_noop_imp, "", NO);
    }
……
}

2.4 非懒加载类的总结

  1. dyld加载链接完所有的库之后,objc开始加载所有的类
  2. objc先从二进制(MachO))文件读取类的方法列表属性列表协议列表等类的原始信息写入ro
  3. 依次读取ro中的方法列表、属性列表及协议列表写入rw
  4. 获取类的分类信息,将分类中的方法列表、属性列表、协议列表从0依次插入到rw中。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260