10 .iOS底层之类的加载

802 阅读16分钟

上篇文章9.iOS底层探索之APP加载流程(DYLD)末尾提到了_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();
    /// c++静态构造方法
    static_init();
    /// 锁的处理,里面是空的,有可能未开源或者说兼容c++那一套
    lock_init();
    /// 初始化异常处理系统
    exception_init();

    _dyld_objc_notify_register(&map_images, load_images, unmap_image);
}

1.environ_init读取影响运行时的环境变量。如果需要,还可以打印环境变量help。

/***********************************************************************
* environ_init
* Read environment variables that affect the runtime.
* Also print environment variable help, if requested.
**********************************************************************/
void environ_init(void) 
{
    ...
 
    // Print OBJC_HELP and OBJC_PRINT_OPTIONS output.
    if (PrintHelp  ||  PrintOptions) {
        if (PrintHelp) {
            _objc_inform("Objective-C runtime debugging. Set variable=YES to enable.");
            _objc_inform("OBJC_HELP: describe available environment variables");
            if (PrintOptions) {
                _objc_inform("OBJC_HELP is set");
            }
            _objc_inform("OBJC_PRINT_OPTIONS: list which options are set");
        }
        if (PrintOptions) {
            _objc_inform("OBJC_PRINT_OPTIONS is set");
        }
        /// 循环打印环境变量,我们可以把判断条件去掉看看会打印些什么
        for (size_t i = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++) {
            const option_t *opt = &Settings[i];            
            if (PrintHelp) _objc_inform("%s: %s", opt->env, opt->help);
            if (PrintOptions && *opt->var) _objc_inform("%s is set", opt->env);
        }
    }
}

可以看到打印了很多环境变量,我们可以设置一些环境变量来打印一些调试信息

OBJC_PRINT_LOAD_METHODS设置为YES就可以打印所有实现了load方法的类,从而处理启动优化,可以在终端输入export OBJC_HELP=1来查看所有系统给我们提供的环境变量以及它们的作用,也可以参考文章

2.tls_init关于线程key的绑定

void tls_init(void)
{
#if SUPPORT_DIRECT_THREAD_KEYS
    _objc_pthread_key = TLS_DIRECT_KEY;
    pthread_key_init_np(TLS_DIRECT_KEY, &_objc_pthread_destroyspecific);
#else
    _objc_pthread_key = tls_create(&_objc_pthread_destroyspecific);
#endif
}

3.static_init

static_init运行c++静态构造函数(只运行系统级别的)。libcdyld调用静态构造函数之前调用_objc_init(),所以我们必须自己做。

/***********************************************************************
* static_init
* Run C++ static constructor functions.
* libc calls _objc_init() before dyld would call our static constructors, 
* so we have to do it ourselves.
**********************************************************************/
static void static_init()
{
    size_t count;
    auto inits = getLibobjcInitializers(&_mh_dylib_header, &count);
    for (size_t i = 0; i < count; i++) {
        inits[i]();
    }
}

4.lock_init

锁的处理,里面是空实现,有可能未开源或者说兼容c、c++那一套

void lock_init(void)
{
}

5.exception_init

初始化libobjc的异常处理系统,注册异常处理的回调,从而监控异常的处理

/***********************************************************************
* exception_init
* Initialize libobjc's exception handling system.
* Called by map_images().
**********************************************************************/
void exception_init(void)
{
    old_terminate = std::set_terminate(&_objc_terminate);
}

出现异常,就会来到这里_objc_terminate

/***********************************************************************
* _objc_terminate
* Custom std::terminate handler.
*
* The uncaught exception callback is implemented as a std::terminate handler. 
* 1. Check if there's an active exception
* 2. If so, check if it's an Objective-C exception
* 3. If so, call our registered callback with the object.
* 4. Finally, call the previous terminate handler.
**********************************************************************/
static void (*old_terminate)(void) = nil;
static void _objc_terminate(void)
{
    if (PrintExceptions) {
        _objc_inform("EXCEPTIONS: terminating");
    }

    if (! __cxa_current_exception_type()) {
        // No current exception.
        (*old_terminate)();
    }
    else {
        // There is a current exception. Check if it's an objc exception.
        @try {
            __cxa_rethrow();
        } @catch (id e) {
            // It's an objc object. Call Foundation's handler, if any.
            (*uncaught_handler)((id)e);
            (*old_terminate)();
        } @catch (...) {
            // It's not an objc object. Continue to C++ terminate.
            (*old_terminate)();
        }
    }
}

6._dyld_objc_notify_register

//
// 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);

仅供objc运行时使用,注册处理程序,以便在映射、取消映射和初始化objc镜像时调用,dyld将会通过一个包含objc-image-info的镜像文件的数组回调mapped函数

  • map_imagesdyldimage加载进内存时,会触发该函数
  • load_imagedyld初始化image会触发该函数
  • unmap_imagedyldimage移除时,会触发该函数 。

二、_read_images

dyldimage加载进内存时,会触发map_images函数

/***********************************************************************
* map_images
* Process the given images which are being mapped in by dyld.
* Calls ABI-agnostic code after taking ABI-specific locks.
*
* Locking: write-locks runtimeLock
**********************************************************************/
void
map_images(unsigned count, const char * const paths[],
           const struct mach_header * const mhdrs[])
{
    mutex_locker_t lock(runtimeLock);
    return map_images_nolock(count, paths, mhdrs);
}

map_images又调用了map_images_nolock

void map_images_nolock(unsigned mhCount, const char * const mhPaths[], const struct mach_header * const mhdrs[])
{
    ...
    if (hCount > 0) {///hCount是镜像文件的个数,调用_read_images来加载镜像文件
        _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
    }
    ...
}

下面重点来了_read_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 (EACH_HEADER) {
        classref_t *classlist = _getObjc2ClassList(hi, &count);
    }
    
    // 3.获取所有的类引用
    for (EACH_HEADER) {
        Class *classrefs = _getObjc2ClassRefs(hi, &count);
    }

    // 4.sel - 方法编号
    for (EACH_HEADER) {
        SEL *sels = _getObjc2SelectorRefs(hi, &count);
    }
    
    // 5.修复旧的objc_msgSend_fixup调用导致一些消息没有处理
    for (EACH_HEADER) {
        message_ref_t *refs = _getObjc2MessageRefs(hi, &count);
    }
    
    // 6.协议
    for (EACH_HEADER) {
        protocol_t **protolist = _getObjc2ProtocolList(hi, &count);
        for (i = 0; i < count; i++) {
            readProtocol(protolist[i], cls, protocol_map, isPreoptimized, isBundle);
        }
    }
    
    // 7.修复协议重映射
    // 获取所有的协议引用
    for (EACH_HEADER) {
       protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
       for (i = 0; i < count; i++) {
           remapProtocolRef(&protolist[i]);
       }
    }
    
    // 8.实现非惰性类(用于+ load方法和静态实例)
     for (EACH_HEADER) {
         classref_t *classlist = _getObjc2NonlazyClassList(hi, &count);
     }
    
    // 9.在CF基础上,实现未来类
    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);
    }
    
    
    // 10.分类
    for (EACH_HEADER) {
        category_t **catlist = _getObjc2CategoryList(hi, &count);
    }
}

1.创建2张表

if (!doneOnce) {
        doneOnce = YES;
        
        ...
        /// 初始化 TaggedPointer
        initializeTaggedPointerObfuscator();
 

        // 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);
        
        ts.log("IMAGE TIMES: first time tasks");
    }
  • gdb_objc_realized_classes: 存储不在共享缓存且已命名的所有类,无论是否已实现,其容量是类数量的4/3
  • allocatedClasses: 存储通过objc_allocateClassPair已分配的所有类和元类的表
// This is a misnomer: gdb_objc_realized_classes is actually a list of 
// named classes not in the dyld shared cache, whether realized or not.
NXMapTable *gdb_objc_realized_classes;  // exported for debuggers in objc-gdb.h

/***********************************************************************
* allocatedClasses
* A table of all classes (and metaclasses) which have been allocated
* with objc_allocateClassPair.
**********************************************************************/
static NXHashTable *allocatedClasses = nil;

2.发现所有的类,修复未解决的未来类。标bundle类。

主要操作是把发现的类存入2张表里

   // Discover classes. Fix up unresolved future classes. Mark bundle classes.

    for (EACH_HEADER) {
        // 从编译后的类列表中取出所有类,获取到的是一个classref_t类型的指针
        classref_t *classlist = _getObjc2ClassList(hi, &count);
        
        if (! mustReadClasses(hi)) {
            // Image is sufficiently optimized that we need not call readClass()
            continue;
        }

        bool headerIsBundle = hi->isBundle();
        bool headerIsPreoptimized = hi->isPreoptimized();
        
        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;
            }
        }
    }

    ts.log("IMAGE TIMES: discover classes");

readClass会返回一个新类,我们进去看看源码

/***********************************************************************
* readClass
* Read a class and metaclass as written by a compiler.
* Returns the new class pointer. This could be: 
* - cls
* - nil  (cls has a missing weak-linked superclass)
* - something else (space for this class was reserved by a future class)
*
* Note that all work performed by this function is preflighted by 
* mustReadClasses(). Do not change this function without updating that one.
*
* Locking: runtimeLock acquired by map_images or objc_readClassPair
**********************************************************************/
Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
{
    const char *mangledName = cls->mangledName();
    ...
    Class replacing = nil;
    ///🌹处理future class
    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) {
        // class list built in shared cache
        // fixme strict assert doesn't work because of duplicates
        // assert(cls == getClass(name));
        assert(getClassExceptSomeSwift(mangledName));
    } else {
    ///🌹存类到表中
        addNamedClass(cls, mangledName, replacing);
        addClassTableEntry(cls);
    }
    ...
    
    return cls;
}

static void addNamedClass(Class cls, const char *name, Class replacing = nil)
{
   ...
        NXMapInsert(gdb_objc_realized_classes, name, cls);
    ...
}

static void addClassTableEntry(Class cls, bool addMeta = true) {
    ...
    if (!isKnownClass(cls))///添加类到表allocatedClasses
        NXHashInsert(allocatedClasses, cls);
    if (addMeta)        ///添加元类到表allocatedClasses
        addClassTableEntry(cls->ISA(), false);
}

3.修复类重映射 一般走不进来

 // Fix up remapped classes
    // Class list and nonlazy class list remain unremapped.
    // Class refs and super refs are remapped for message dispatching.
    
    // 主要是修复重映射 - 一般走不进来
    // 将未映射Class和Super Class重映射,被remap的类都是非懒加载的类
    if (!noClassesRemapped()) {
        for (EACH_HEADER) {
            // 重映射Class,注意是从_getObjc2ClassRefs函数中取出类的引用
            Class *classrefs = _getObjc2ClassRefs(hi, &count);
            for (i = 0; i < count; i++) {
                remapClassRef(&classrefs[i]);
            }
            // fixme why doesn't test future1 catch the absence of this?
            classrefs = _getObjc2SuperRefs(hi, &count);
            for (i = 0; i < count; i++) {
                remapClassRef(&classrefs[i]);
            }
        }
    }

    ts.log("IMAGE TIMES: remap classes");

4.添加SELnamedSelectors

通过_getObjc2SelectorRefs拿到MachO中的静态段__objc_selrefs,遍历列表调用sel_registerNameNoLockSEL添加到namedSelectors哈希表

 // 将所有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);
            }
        }
    }

    ts.log("IMAGE TIMES: fix up selector references");

看看sel_registerNameNoLock源码

SEL sel_registerNameNoLock(const char *name, bool copy) {
    return __sel_registerName(name, 0, copy);  // NO lock, maybe copy
}

static SEL __sel_registerName(const char *name, bool shouldLock, bool copy) 
{
    ...
    if (!namedSelectors) {
        namedSelectors = NXCreateMapTable(NXStrValueMapPrototype, 
                                          (unsigned)SelrefCount);
    }
    if (!result) {
        result = sel_alloc(name, copy);
        // fixme choose a better container (hash not map for starters)
        NXMapInsert(namedSelectors, sel_getName(result), result);
    }

    return result;
}

5.修复旧的函数指针调用遗留

通过_getObjc2MessageRefs获取到静态段__objc_selrefsfixupMessageRef遍历将函数指针进行注册,并fix为新的函数指针

#if SUPPORT_FIXUP
    // Fix up old objc_msgSend_fixup call sites
    // 修复旧的函数指针调用遗留
    for (EACH_HEADER) {
        message_ref_t *refs = _getObjc2MessageRefs(hi, &count);
        if (count == 0) continue;

        if (PrintVtables) {
            _objc_inform("VTABLES: repairing %zu unsupported vtable dispatch "
                         "call sites in %s", count, hi->fname());
        }
        for (i = 0; i < count; i++) {
             // 内部将常用的alloc、objc_msgSend等函数指针进行注册,并fix为新的函数指针
            fixupMessageRef(refs+i);
        }
    }

    ts.log("IMAGE TIMES: fix up objc_msgSend_fixup");
#endif

6.将所有的Protocol都添加到protocol_map表中

调用_getObjc2ProtocolList获取到__objc_protolist协议列表,readProtocol遍历添加Protocolprotocol_map哈希表

// Discover protocols. Fix up protocol refs.
// 遍历所有协议列表,并且将协议列表加载到Protocol的哈希表中
for (EACH_HEADER) {
    extern objc_class OBJC_CLASS_$_Protocol;
    // cls = Protocol类,所有协议和对象的结构体都类似,isa都对应Protocol类
    Class cls = (Class)&OBJC_CLASS_$_Protocol;
    assert(cls);
    // 获取protocol哈希表
    NXMapTable *protocol_map = protocols();
    bool isPreoptimized = hi->isPreoptimized();
    bool isBundle = hi->isBundle();

    // 从编译器中读取并初始化Protocol
    protocol_t **protolist = _getObjc2ProtocolList(hi, &count);
    for (i = 0; i < count; i++) {
        readProtocol(protolist[i], cls, protocol_map, 
                     isPreoptimized, isBundle);
    }
}

ts.log("IMAGE TIMES: discover protocols");

7.修复协议列表引用

通过_getObjc2ProtocolRefs获取到__objc_protorefs(与__objc_protolist不是同一个东西),遍历remapProtocolRef修复协议,remapProtocolRef比较当前协议和协议列表中同一内存地址的协议是否相同,如果不同则替换

 // Fix up @protocol references
    // Preoptimized images may have the right 
    // answer already but we don't know for sure.
    // 修复协议列表引用,优化后的images可能是正确的,但是并不确定
    for (EACH_HEADER) {
        // 需要注意到是,下面的函数是_getObjc2ProtocolRefs,和上面的_getObjc2ProtocolList不一样
        protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
        for (i = 0; i < count; i++) {
            remapProtocolRef(&protolist[i]);
        }
    }

    ts.log("IMAGE TIMES: fix up @protocol references");

8.实现非懒加载的类(实现了load方法)

所以实现了+load方法的类是非懒加载类,否则就是懒加载类

_getObjc2NonlazyClassList获取到__objc_nlclslist数据段,取出非懒加载类 addClassTableEntry再加载一遍,如果已添加就不会添加进去,确保整个结构都被添加 realizeClassWithoutSwift实例化类对象的一些信息

// 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;

            // hack for class __ARCLite__, which didn't get this above
#if TARGET_OS_SIMULATOR
            if (cls->cache._buckets == (void*)&_objc_empty_cache  &&  
                (cls->cache._mask  ||  cls->cache._occupied)) 
            {
                cls->cache._mask = 0;
                cls->cache._occupied = 0;
            }
            if (cls->ISA()->cache._buckets == (void*)&_objc_empty_cache  &&  
                (cls->ISA()->cache._mask  ||  cls->ISA()->cache._occupied)) 
            {
                cls->ISA()->cache._mask = 0;
                cls->ISA()->cache._occupied = 0;
            }
#endif
            
            addClassTableEntry(cls);

            if (cls->isSwiftStable()) {
                if (cls->swiftMetadataInitializer()) {
                    _objc_fatal("Swift class %s with a metadata initializer "
                                "is not allowed to be non-lazy",
                                cls->nameForLogging());
                }
                // fixme also disallow relocatable classes
                // We can't disallow all Swift classes because of
                // classes like Swift.__EmptyArrayStorage
            }
            // 实现所有非懒加载的类(实例化类对象的一些信息,例如rw)
            realizeClassWithoutSwift(cls);
        }
    }

    ts.log("IMAGE TIMES: realize non-lazy classes");

重点看下realizeClassWithoutSwift

8.1 realizeClassWithoutSwift

static Class realizeClassWithoutSwift(Class cls)
{
    runtimeLock.assertLocked();

    const class_ro_t *ro;
    class_rw_t *rw;
    Class supercls;
    Class metacls;
    bool isMeta;

    if (!cls) return nil;
    if (cls->isRealized()) return cls;
    assert(cls == remapClass(cls));

    // fixme verify class is not in an un-dlopened part of the shared cache?

    ro = (const class_ro_t *)cls->data();
    if (ro->flags & RO_FUTURE) {///🌹未来类的处理
        // This was a future class. rw data is already allocated.
        rw = cls->data();
        ro = cls->data()->ro;
        cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
    } else {///🌹🌹🌹正常类的处理 rw初始化 将ro拷贝一份到rw中的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);
    }

    isMeta = ro->flags & RO_META;

    rw->version = isMeta ? 7 : 0;  // old runtime went up to 6


    // Choose an index for this class.
    // Sets cls->instancesRequireRawIsa if indexes no more indexes are available
    cls->chooseClassArrayIndex();

    ...
    ///🌹🌹🌹递归实现父类和元类  s
    upercls递归的出口在上面if (!cls) return nil; 根类继承nil,
    metacls递归的出口在remapClass
    
    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;
}


static Class remapClass(Class cls) {
    runtimeLock.assertLocked();

    Class c2;

    if (!cls) return nil;

    NXMapTable *map = remappedClasses(NO);
    /// 🌹🌹🌹如果表中已有该类,则返回一个空值;如果没有则返回当前类,这样保证了类只加载一次并结束递归
    if (!map  ||  NXMapMember(map, cls, (void**)&c2) == NX_MAPNOTAKEY) {
        return cls;
    } else {
    /// c2为nil
        return c2;
    }
}

realizeClassWithoutSwift主要做了:

  • 初始化rw并将ro拷贝一份到rw
  • 递归实现父类和元类
  • 更新父类和元类的归属关系
  • 将这个类连接到它的超类的子类列表
  • methodizeClass附加类别

8.2 methodizeClass

修复cls的方法列表、协议列表和属性列表,附上任何未完成的类别。

/***********************************************************************
* 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)
{
   ...
    // Install methods and properties that the class implements itself.
    /// 🌹🌹🌹从ro中读取方法列表(包括分类中的方法)、属性列表、协议列表赋值给rw
    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);
    }

    // Attach categories.
    category_list *cats = unattachedCategoriesForClass(cls, true /*realizing*/);
    attachCategories(cls, cats, false /*don't flush caches*/);
...
}

8.3 attachLists

attachLists可以添加方法,属性,协议,说明他们内部的结构都是差不多的

struct method_list_t : entsize_list_tt<method_t, method_list_t, 0x3> 

struct property_list_t : entsize_list_tt<property_t, property_list_t, 0>  

struct protocol_list_t {
    uintptr_t count;// count is 64-bit by accident. 
    protocol_ref_t list[0]; // variable-size

   void attachLists(List* const * addedLists, uint32_t addedCount) {
        if (addedCount == 0) return;

        if (hasArray()) {
            // many lists -> many lists
            uint32_t oldCount = array()->count;
            uint32_t newCount = oldCount + addedCount;
            setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
            array()->count = newCount;
            memmove(array()->lists + addedCount, array()->lists, 
                    oldCount * sizeof(array()->lists[0]));
            memcpy(array()->lists, addedLists, 
                   addedCount * sizeof(array()->lists[0]));
        }
        else if (!list  &&  addedCount == 1) {
            // 0 lists -> 1 list
            list = addedLists[0];
        } 
        else {
            // 1 list -> many lists
            List* oldList = list;
            uint32_t oldCount = oldList ? 1 : 0;
            uint32_t newCount = oldCount + addedCount;5
            setArray((array_t *)malloc(array_t::byteSize(newCount)));
            array()->count = newCount;
            if (oldList) array()->lists[addedCount] = oldList;
            memcpy(array()->lists, addedLists, 
                   addedCount * sizeof(array()->lists[0]));
        }
    }

attachLists可以分为3中情况:

  • (多对多)当前调用attachListslist_array_tt二维数组中有多个一维数组
通过`realloc`对容器进行重新分配大小为原来的大小加上新增的大小
通过`memmove`把原来的数据移动到容器的末尾
通过`memcpy`把新的数据拷贝到容器的起始位置
  • (0对1)如果调用attachListslist_array_tt二维数组为空且新增大小数目为 1
直接赋值addedList的第一个list
  • (1对多)如果调用attachListslist_array_tt二维数组只有一个一维数组
通过realloc对容器进行重新分配大小为原来的大小加上新增的大小
由于只有一个一维数组,所以直接赋值到新Array的最后一个位置
把新的数据memcpy拷贝到容器的起始位置

memmovememcpy的区别:

  • 在不知道需要平移的内存大小时,需要memmove进行内存平移,保证安全
  • memcpy从原内存地址的起始位置开始拷贝若干个字节到目标内存地址中,速度快

9.实现所有懒加载的类

// 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);
}    

ts.log("IMAGE TIMES: realize future classes");

由于懒加载类并没有实现load方法,所以不需要在启动的时候就加载到内存中,那么懒加载类是什么时候加载到内存中的呢?

非懒加载类会递归寻找父类,并进行加载,所以如果一个懒加载类是父类,则一定会在递归中被加载,注意子类并不会哦

如果这个类调用了方法,进行了消息的发送,说明此时是肯定已经加载的。

寻找函数lookUpImpOrForward,我们发现有一个!cls->isRealized()判断,是判断类有没有加载

if (!cls->isRealized()) {
    cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
    // runtimeLock may have been dropped but is now locked again
}

发现如下调用流程:

lookUpImpOrForward -> realizeClassMaybeSwiftAndLeaveLocked -> realizeClassMaybeSwiftMaybeRelock -> realizeClassWithoutSwift

小结:懒加载类的加载,是在第一次被调用(发送消息)的时候才进行加载,并不是启动的时候就加载进内存

10.发现和处理所有Category

分类必须在最后处理,以避免潜在的竞争,分类的加载分了4中情况,篇幅比较长,将会在下篇文字展开分析

// Discover categories.
    // 发现和处理所有Category
    for (EACH_HEADER) {
        // 外部循环遍历找到当前类,查找类对应的Category数组
        category_t **catlist = 
            _getObjc2CategoryList(hi, &count);
        bool hasClassProperties = hi->info()->hasCategoryClassProperties();

        for (i = 0; i < count; i++) {
            // 内部循环遍历当前类的所有Category
            category_t *cat = catlist[i];
            Class cls = remapClass(cat->cls);

            if (!cls) {
                // Category's target class is missing (probably weak-linked).
                // Disavow any knowledge of this category.
                catlist[i] = nil;
                if (PrintConnecting) {
                    _objc_inform("CLASS: IGNORING category \?\?\?(%s) %p with "
                                 "missing weak-linked target class", 
                                 cat->name, cat);
                }
                continue;
            }

            // Process this category. 
            // First, register the category with its target class. 
            // Then, rebuild the class's method lists (etc) if 
            // the class is realized.
            // 首先,通过其所属的类注册Category。如果这个类已经被实现,则重新构造类的方法列表。
            bool classExists = NO;
            if (cat->instanceMethods ||  cat->protocols  
                ||  cat->instanceProperties) 
            {
                // 将Category添加到对应Class的value中,value是Class对应的所有category数组
                addUnattachedCategoryForClass(cat, cls, hi);
                // 将Category的method、protocol、property添加到Class
                if (cls->isRealized()) {
                    remethodizeClass(cls);
                    classExists = YES;
                }
                if (PrintConnecting) {
                    _objc_inform("CLASS: found category -%s(%s) %s", 
                                 cls->nameForLogging(), cat->name, 
                                 classExists ? "on existing class" : "");
                }
            }

            // 这块和上面逻辑一样,区别在于这块是对Meta Class做操作,而上面则是对Class做操作
            // 根据下面的逻辑,从代码的角度来说,是可以对原类添加Category的
            if (cat->classMethods  ||  cat->protocols  
                ||  (hasClassProperties && cat->_classProperties)) 
            {
                addUnattachedCategoryForClass(cat, cls->ISA(), hi);
                if (cls->ISA()->isRealized()) {
                    remethodizeClass(cls->ISA());
                }
                if (PrintConnecting) {
                    _objc_inform("CLASS: found category +%s(%s)", 
                                 cls->nameForLogging(), cat->name);
                }
            }
        }
    }

    ts.log("IMAGE TIMES: discover categories");

    // Category discovery MUST BE LAST to avoid potential races 
    // when other threads call the new category code before 
    // this thread finishes its fixups.

    // +load handled by prepare_load_methods()

    // 初始化从磁盘中加载的所有类,发现Category必须是最后执行的

tips

  • rw表示readWrite,由于动态性,可能会往类中添加属性、方法、协议
  • ro表示readOnly,在编译时已经确定了内存