阅读 522

iOS的OC类的加载

前言

笔者整理了一系列有关OC的底层文章,希望可以帮助到你。

1.iOS的OC对象创建的alloc原理

2.iOS的OC对象的内存对齐

3.iOS的OC的isa的底层原理

4.iOS的OC源码分析之类的结构分析

5.iOS的OC的方法缓存的源码分析

6.iOS的OC的方法的查找原理

7.iOS的OC的方法的决议与消息转发原理

8.iOS的App的加载流程

dyld的加载流程中,从dyld源码过度到objc源码的过程是在objc_init这个函数。接下来会先介绍objc_init

1. objc_init

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();
    static_init();
    lock_init();
    exception_init();

    _dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
复制代码

_objc_init函数是在运行时执行到的,下面对里面的各个函数的简单介绍。

  • environ_init():读取影响运行时的环境变量,这些环境变量可以在Xcode中设置打印出来。
  • tls_init():关于线程的key的绑定。比如线程数据的析构函数。
  • static_init():初始化调用系统的c++的构造函数。
  • lock_init():这个什么都没有实现,可能c++层面的锁也适合oc(只是猜想)。
  • exception_init():异常初始化,监控异常的回调,就是异常出错会回调在这个函数里面的_objc_terminate
  • _dyld_objc_notify_register:是dyld加载映射回调到objc的函数,并且这个也是_objc_init函数中最主要的函数。

接下来就是对_dyld_objc_notify_register函数中的map_images来做详细的介绍,并且类的加载也是在这里。

1.1 map_images

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

void 
map_images_nolock(unsigned mhCount, const char * const mhPaths[],
                  const struct mach_header * const mhdrs[])
{
.....省略部分代码.......

    // Find all images with Objective-C metadata.
    hCount = 0;

    // Count classes. Size various table based on the total.
    int totalClasses = 0;
    int unoptimizedTotalClasses = 0;
.....省略部分代码.......
    if (hCount > 0) {
        _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
    }
}
复制代码

map_images函数中调用map_images_nolock函数,其中_read_images函数是这些函数中最重要的。由函数名称可以知道是读取镜像文件到内存中。

  • hList:镜像头文件的信息列表。
  • hCount:所有的镜像的元数据的数量。
  • totalClasses:所有类的总数量。
  • unoptimizedTotalClasses:没有优化的类的总数量

2. _read_images

由于_read_images代码过多,只能部分代码来解析。

if (!doneOnce) {
    doneOnce = YES;
...省略部分代码..

        if (DisableTaggedPointers) {
            disableTaggedPointers();
        }
        
        initializeTaggedPointerObfuscator();

        if (PrintConnecting) {
            _objc_inform("CLASS: found %d classes during launch", totalClasses);
        }

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


// 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;
复制代码

doneOnce只是执行一次,如果是初次进来的就会执行括号里面的代码。 initializeTaggedPointerObfuscator()这个函数是对TaggedPointer做优化的。其中gdb_objc_realized_classesallocatedClasses是两张哈希表,gdb_objc_realized_classes这张表保存的是主要不是共享缓存里面的类,无论实现了还是没有实现的类都在这张表里面;allocatedClasses这张表保存的是所有被分配内存后的类和元类。从中可以看出gdb_objc_realized_classes是包含了allocatedClasses的内容。

2.1 所有类的重映射

    // 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");
复制代码

这段代码主要是从编译后的类列表中读取所有类的classref_t指针,通过遍历出类地址cls,通过readClass函数读取编译器的类和元类信息。

2.1.1 readClass函数
Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
{
    const char *mangledName = cls->mangledName();
    
    if (missingWeakSuperclass(cls)) {
        // No superclass (probably weak-linked). 
        // Disavow any knowledge of this subclass.
        if (PrintConnecting) {
            _objc_inform("CLASS: IGNORING class '%s' with "
                         "missing weak-linked superclass", 
                         cls->nameForLogging());
        }
        addRemappedClass(cls, nil);
        cls->superclass = nil;
        return nil;
    }
    
    // Note: Class __ARCLite__'s hack does not go through here. 
    // Class structure fixups that apply to it also need to be 
    // performed in non-lazy realization below.
    
    // These fields should be set to zero because of the 
    // binding of _objc_empty_vtable, but OS X 10.8's dyld 
    // does not bind shared cache absolute symbols as expected.
    // This (and the __ARCLite__ hack below) can be removed 
    // once the simulator drops 10.8 support.
#if TARGET_OS_SIMULATOR
    if (cls->cache._mask) cls->cache._mask = 0;
    if (cls->cache._occupied) cls->cache._occupied = 0;
    if (cls->ISA()->cache._mask) cls->ISA()->cache._mask = 0;
    if (cls->ISA()->cache._occupied) cls->ISA()->cache._occupied = 0;
#endif
    cls->fixupBackwardDeployingStableSwift();
    Class replacing = nil;
    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);
    }

    // for future reference: shared cache never contains MH_BUNDLEs
    if (headerIsBundle) {
        cls->data()->flags |= RO_FROM_BUNDLE;
        cls->ISA()->data()->flags |= RO_FROM_BUNDLE;
    }
    
    return cls;
}

static void addNamedClass(Class cls, const char *name, Class replacing = nil)
{
    runtimeLock.assertLocked();
    Class old;
    if ((old = getClassExceptSomeSwift(name))  &&  old != replacing) {
        inform_duplicate(name, old, cls);

        // getMaybeUnrealizedNonMetaClass uses name lookups.
        // Classes not found by name lookup must be in the
        // secondary meta->nonmeta table.
        addNonMetaClass(cls);
    } else {
        NXMapInsert(gdb_objc_realized_classes, name, cls);
    }
    assert(!(cls->data()->flags & RO_META));

    // wrong: constructed classes are already realized when they get here
    // assert(!cls->isRealized());
}

static void addClassTableEntry(Class cls, bool addMeta = true) {
    runtimeLock.assertLocked();

    // This class is allowed to be a known class via the shared cache or via
    // data segments, but it is not allowed to be in the dynamic table already.
    assert(!NXHashMember(allocatedClasses, cls));

    if (!isKnownClass(cls))
        NXHashInsert(allocatedClasses, cls);
    if (addMeta)
        addClassTableEntry(cls->ISA(), false);
}


复制代码

readClass函数中,if (Class newCls = popFutureNamedClass(mangledName))这个判断的是对未来需要处理的类才会进入到if条件的流程中,一般的情况下是不会进入这个流程的。并且这个函数中最主要的还是执行了addNamedClass(cls, mangledName, replacing);addClassTableEntry(cls);这两个函数,由源码可以知道,addNamedClass函数是将类的名字和地址添加到gdb_objc_realized_classes这个总的哈希表中,addClassTableEntry函数是将类的地址添加到allocatedClasses这张子的哈希表。此时就是将类的地址信息等插入到哈希表中了。从readClass函数返回的Class,再次回到Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);这段代码,其中在上面的源码中有newClscls对比的判断,意思就是如果从readClass函数返回的newCls和获取到的cls不相等(只有在进入popFutureNamedClass的判断条件才会不想等的,因为这里面对rw中的ro做了处理)。说明此时的newCls与一般的类不一样了,之后会执行到如下的源码:

    // Realize newly-resolved future classes, in case CF manipulates them
    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");
复制代码

但是一般情况下是不会执行的。这个是未来需要特殊处理的而已。综合得出的是在readClass函数中最要是判断当前的cls是不是后期需要处理的类,如果是就要读取cls的data()设置ro/rw(一般情况下不会的),并且将类的地址信息等分别插入到gdb_objc_realized_classesallocatedClasses两张哈希表中。

2.2 修复类的重映射

    // Fix up remapped classes
    // Class list and nonlazy class list remain unremapped.
    // Class refs and super refs are remapped for message dispatching.
    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]);
            }
        }
    }
复制代码

这个代码块主要是修复类的重映射,就是将未映射的Class和Super Class重映射,其中被remap的类都是非懒加载的类。这个代码块一般情况下是不会被执行到的。

2.3 注册所有方法的SEL到namedSelectors表

    // 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 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) 
{
    SEL result = 0;

    if (shouldLock) selLock.assertUnlocked();
    else selLock.assertLocked();

    if (!name) return (SEL)0;

    result = search_builtins(name);
    if (result) return result;
    
    conditional_mutex_locker_t lock(selLock, shouldLock);
    if (namedSelectors) {
        result = (SEL)NXMapGet(namedSelectors, name);
    }
    if (result) return result;

    // No match. Insert.

    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;
}
复制代码

这段代码就是将里面的方法的SEL通过_getObjc2SelectorRefs函数获取到SEL数组从中得到方法的名字,然后通过sel_registerNameNoLock函数再次调用__sel_registerName函数,将方法名都注册到namedSelectors这张哈希表中。

2.3 将所有Protocol注册到protocol_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");
复制代码

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

#if !(defined(__x86_64__) && (TARGET_OS_OSX || TARGET_OS_SIMULATOR))
#   define SUPPORT_FIXUP 0
#else
#   define SUPPORT_FIXUP 1
#endif

#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++) {
            fixupMessageRef(refs+i);
        }
    }

    ts.log("IMAGE TIMES: fix up objc_msgSend_fixup");
#endif
复制代码

这段代码块一般情况下是不会执行的,其中fixupMessageRef函数内部将常用的alloc、objc_msgSend等函数指针进行注册,并fix为新的函数指针。

2.5 实现非懒加载类

    // Realize non-lazy classes (for +load methods and static instances)
    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");
复制代码

所谓的非懒加载类就是实现了+load类方法,在这段源码中对非懒加载类的bitsdata()里面的rw操作就是在realizeClassWithoutSwift这个函数里面。

2.5.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 {
        // 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();

    if (PrintConnecting) {
        _objc_inform("CLASS: realizing class '%s'%s %p %p #%u %s%s",
                     cls->nameForLogging(), isMeta ? " (meta)" : "", 
                     (void*)cls, ro, cls->classArrayIndex(),
                     cls->isSwiftStable() ? "(swift)" : "",
                     cls->isSwiftLegacy() ? "(pre-stable swift)" : "");
    }

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

#if SUPPORT_NONPOINTER_ISA
    // Disable non-pointer isa for some classes and/or platforms.
    // Set instancesRequireRawIsa.
    bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
    bool rawIsaIsInherited = false;
    static bool hackedDispatch = false;

    if (DisableNonpointerIsa) {
        // Non-pointer isa disabled by environment or app SDK version
        instancesRequireRawIsa = true;
    }
    else if (!hackedDispatch  &&  !(ro->flags & RO_META)  &&  
             0 == strcmp(ro->name, "OS_object")) 
    {
        // hack for libdispatch et al - isa also acts as vtable pointer
        hackedDispatch = true;
        instancesRequireRawIsa = true;
    }
    else if (supercls  &&  supercls->superclass  &&  
             supercls->instancesRequireRawIsa()) 
    {
        // This is also propagated by addSubclass() 
        // but nonpointer isa setup needs it earlier.
        // Special case: instancesRequireRawIsa does not propagate 
        // from root class to root metaclass
        instancesRequireRawIsa = true;
        rawIsaIsInherited = true;
    }
    
    if (instancesRequireRawIsa) {
        cls->setInstancesRequireRawIsa(rawIsaIsInherited);
    }
// SUPPORT_NONPOINTER_ISA
#endif

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

    // Reconcile instance variable offsets / layout.
    // This may reallocate class_ro_t, updating our ro variable.
    if (supercls  &&  !isMeta) reconcileInstanceVariables(cls, supercls, ro);

    // Set fastInstanceSize if it wasn't set already.
    cls->setInstanceSize(ro->instanceSize);

    // Copy some flags from ro to rw
    if (ro->flags & RO_HAS_CXX_STRUCTORS) {
        cls->setHasCxxDtor();
        if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
            cls->setHasCxxCtor();
        }
    }
    
    // Propagate the associated objects forbidden flag from ro or from
    // the superclass.
    if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) ||
        (supercls && supercls->forbidsAssociatedObjects()))
    {
        rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
    }

    // Connect this class to its superclass's subclass lists
    if (supercls) {
        addSubclass(supercls, cls);
    } else {
        addRootClass(cls);
    }

    // Attach categories
    methodizeClass(cls);

    return cls;
}
复制代码

这部分的源码就是通过cls->data()得到ro,因为if (ro->flags & RO_FUTURE)这个判断条件里面的代码一般情况下是不会执行的,所以只会执行else里面的,在else代码块里面只是初始化rw,将其set到data()里面而已,此时rw里面的method_array_t methodsproperty_array_t propertiesprotocol_array_t protocols还是没有值的。继续往下是

    supercls = realizeClassWithoutSwift(remapClass(cls->superclass));
    metacls = realizeClassWithoutSwift(remapClass(cls->ISA()));
复制代码

此时就是对父类元类的类信息进行递归,此时就可以参考一下isa的走位图就可以很明白此时的递归了。在下面的代码插入到继承链和isa的走位链

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

并且递归的结束点是在函数开始的地方

    if (!cls) return nil;
    if (cls->isRealized()) return cls;
复制代码

因为最终的父类的根类是继承NSObject,元类的根元类是也是继承NSObject,而NSObject的上一级终点是nil。这里就很好地说明了这一切。

    // Connect this class to its superclass's subclass lists
    if (supercls) {
        addSubclass(supercls, cls);
    } else {
        addRootClass(cls);
    }
复制代码

上面这段代码就是对父类和子类之间进行类似双向链表的形式进行关联。接着就是执行methodizeClass函数了。

2.5.2 methodizeClass函数
static void methodizeClass(Class cls)
{
    runtimeLock.assertLocked();

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

    // Methodizing for the first time
    if (PrintConnecting) {
        _objc_inform("CLASS: methodizing class '%s' %s", 
                     cls->nameForLogging(), isMeta ? "(meta)" : "");
    }

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

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

    if (PrintConnecting) {
        if (cats) {
            for (uint32_t i = 0; i < cats->count; i++) {
                _objc_inform("CLASS: attached category %c%s(%s)", 
                             isMeta ? '+' : '-', 
                             cls->nameForLogging(), cats->list[i].cat->name);
            }
        }
    }
    
    if (cats) free(cats);

#if DEBUG
    // Debug: sanity-check all SELs; log method list contents
    for (const auto& meth : rw->methods) {
        if (PrintConnecting) {
            _objc_inform("METHOD %c[%s %s]", isMeta ? '+' : '-', 
                         cls->nameForLogging(), sel_getName(meth.name));
        }
        assert(sel_registerName(sel_getName(meth.name)) == meth.name); 
    }
#endif
}
复制代码

methodizeClass函数中可以看到,此时从cls->data()获取到的rw里面的method_list_t,property_list_tprotocol_list_t的值还是空的。需要从rw中的ro获取到它们的值,并分别对rw中的methods,propertiesprotocols进行赋值,这个时候才是真正的对rw里面的属性进行赋值。这些操作都是通过attachLists函数来完成的。

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

        if (hasArray()) {
            // many lists -> many lists
            uint32_t oldCount = array()->count;//10
            uint32_t newCount = oldCount + addedCount;//4
            setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
            array()->count = newCount;// 10+4
   
            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;
            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函数中分别有多对多0对11对多这三种情况。

其中,memmove和memcpy函数作用都是拷贝一定长度的内存内容。当内存发生局部重叠时memmove函数能够保证拷贝结果的正确性,而memcpy则不能保证拷贝结果的正确性;当内存没有发生重叠的时候两个函数的结果是一样的。

  • 多对多:先对数组进行扩容,用memmove函数将旧的数组列表添加到扩容后的数组中(memmove可以去掉重叠的内存),然后用memcpy函数将需要添加的列表添加在旧的列表的前面。所以新添加的列表是在旧的列表的前面的。
  • 0对1:直接对列表赋值了。
  • 1对多:先判断旧数组是否有值,如果有数组的长度也只是1,然后进行扩容,在旧数组有值的情况下,将旧数组放到新扩容数组最后的下标值保存,然后用memcpy函数进行拷贝。

其中attachLists函数除了在加载类处理方法,属性和协议的时候会被调用,还在添加方法addMethods函数中,添加属性_class_addProperty,添加协议class_addProtocol和分类的加载attachCategories中都会被调用。

2.6 懒加载类的实现

懒加载类和非懒加载类的主要区别就是是否实现了+load方法,上面介绍了非懒加载类,那么懒加载类是怎么实现的呢?其实懒加载类就是在调用的时候才实现的,那么这就是在该类第一次调用方法的时候会被初始化加载进来,这个时候就需要去到lookUpImpOrForward这个函数里面了。如果想了解这个函数的可以看一下iOS的OC的方法的查找原理 这篇文章的介绍。其中有这么一段源码

    if (!cls->isRealized()) {
        cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
        // runtimeLock may have been dropped but is now locked again
    }
  ================================ 
    static Class
realizeClassMaybeSwiftAndLeaveLocked(Class cls, mutex_t& lock)
{
    return realizeClassMaybeSwiftMaybeRelock(cls, lock, true);
}

static Class
realizeClassMaybeSwiftMaybeRelock(Class cls, mutex_t& lock, bool leaveLocked)
{
    lock.assertLocked();

    if (!cls->isSwiftStable_ButAllowLegacyForNow()) {
        // Non-Swift class. Realize it now with the lock still held.
        // fixme wrong in the future for objc subclasses of swift classes
        realizeClassWithoutSwift(cls);
        if (!leaveLocked) lock.unlock();
    } else {
        // Swift class. We need to drop locks and call the Swift
        // runtime to initialize it.
        lock.unlock();
        cls = realizeSwiftClass(cls);
        assert(cls->isRealized());    // callback must have provoked realization
        if (leaveLocked) lock.lock();
    }

    return cls;
}

复制代码

从源码中可以看到,在方法查找的过程中,如果当前的cls没有实现的话,就会执行realizeClassMaybeSwiftAndLeaveLocked函数,最终会执行到realizeClassMaybeSwiftMaybeRelock函数中。在这个函数中分别有Non-Swift classSwift class的判断,当前的是OC的类,最终还是会执行到realizeClassWithoutSwift函数,而这个函数在上面也介绍了是对rw和ro的操作的。

3. 最后

通过上面的一系列源码的分析了类的加载整个过程,接下来总结一下:

  • dyld加载过程中,从dyld过度到objc是在_objc_init函数中_dyld_objc_notify_register函数回调回来的。
  • 通过_read_images函数读取镜像文件的内容到内存中,在并且在这个过程中如果是初次进入会分别初始化gdb_objc_realized_classesallocatedClasses两张哈希表,其中加载所有的类在gdb_objc_realized_classes表中,分配内存的类也会在allocatedClasses表中。
  • 对所有的类做重映射。
  • 将所有的SEL都注册到namedSelectors表中。
  • 修复函数指针遗留。
  • 将所有Protocol注册到protocol_map表。
  • 对所有的Protocol做重映射。
  • 初始化所有的非懒加载类,进行rwro操作。
  • 遍历已标记的懒加载类,并做初始化操作。
  • 处理所有的Category,包括ClassMetaClass