类、分类的加载

上篇文章应用程序加载分析了dyldmain()函数的大体流程,这篇文章主要分析_objc_init()到类的处理流程。

1._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();
    // 关于线程key的绑定 比如线程数据的析构函数
    tls_init();
    // 运行 C++ 静态构造函数。在dyld调用我们的静态构造函数之前,libc会调用 _objc_init(),因此需要自己做
    static_init();
    // runtime运行时环境初始化,主要是unattachedCategories、allocatedClasses方法
    runtime_init();
    // 初始化 libObjc的异常处理系统
    exception_init();
    // 缓存条件初始化
    cache_init();
    // 启动回调机制,通常不会做什么,因为所有的初始化都是惰性的,但是对于某些进程我们会迫不及待的加载trampolines dylib。
    _imp_implementationWithBlock_init();
    // 注册回调 map_images 管理文件和动态库中的所有符号(class Protocol selector category) load_image 加载执行load方法
    _dyld_objc_notify_register(&map_images, load_images, unmap_image);

#if __OBJC2__
    didCallDyldNotifyRegister = true;
#endif
}

环境变量可通过修改源码工程代码打印:

void environ_init(void) 
{
....省略
    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);
        _objc_inform("%s is set", opt->env);
    }
}

打印结果:


environ_init1.png

OBJC_PRINT_LOAD_METHODS 设置了会打印所有执行+(void)load方法的类,举个

environ_init2.png

environ_init3.png

2._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 运行时使用。
  • 注册回调,当镜像文件被mapped--映射--&map_imagesunmapped--取消映射--unmap_imageinit--初始化--load_images

先看下map_images方法,通过源码分析可知其核心方法为map_images->map_images_nolock-> _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.控制一次性加载条件
    if (!doneOnce) {
    ...
            // namedClasses
        // Preoptimized classes don't go in this table.
        // 4/3 is NXMapTable's load factor
        int namedClassesSize = 
            (isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
        // 创建哈希表 储存不在共享缓存且已命名的类,不论是否实现,容量为类容量的3/4.
        gdb_objc_realized_classes =
            NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);

        ts.log("IMAGE TIMES: first time tasks");
    ...
    }
    // Fix up @selector references
    // 2.修复预编译阶段@selector错乱问题 sel是带地址的字符串
    static size_t UnfixedSelectors;
    {
        mutex_locker_t lock(selLock);
        for (EACH_HEADER) {
            if (hi->hasPreoptimizedSelectors()) continue;

            bool isBundle = hi->isBundle();
            // 拿到Mach-O中的静态段__objc_selrefs
            SEL *sels = _getObjc2SelectorRefs(hi, &count);
            UnfixedSelectors += count;
            for (i = 0; i < count; i++) {
                const char *name = sel_cname(sels[i]);
                // 注册 sel
                SEL sel = sel_registerNameNoLock(name, isBundle);
                // 如果地址不相同改为一致
                if (sels[i] != sel) {
                    sels[i] = sel;
                }
            }
        }
    }
...
 // 3、错误混乱的类处理
    bool hasDyldRoots = dyld_shared_cache_some_image_overridden();

    for (EACH_HEADER) {
        if (! mustReadClasses(hi, hasDyldRoots)) {
            // Image is sufficiently optimized that we need not call readClass()
            continue;
        }
        // 从Mach-O中获取静态段__objc_classlist
        classref_t const *classlist = _getObjc2ClassList(hi, &count);

        bool headerIsBundle = hi->isBundle();
        bool headerIsPreoptimized = hi->hasPreoptimizedClasses();

        for (i = 0; i < count; i++) {
            // 此时类只是一个地址
            Class cls = (Class)classlist[i];
            // 变为类名+地址
            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;
            }
        }
    }
...
    // Fix up remapped classes
    // Class list and nonlazy class list remain unremapped.
    // Class refs and super refs are remapped for message dispatching.
    // 4、修复重映射一些没有被镜像文件加载进来的类 经调试没有进入
    if (!noClassesRemapped()) {
        for (EACH_HEADER) {
            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]);
            }
        }
    }
...
    // 5、修复一些消息
    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");
...
   // 6、当类里面有协议时:readProtocol 读取协议 遍历寻找所有的协议列表添加到protocol_map哈希表
    for (EACH_HEADER) {
        extern objc_class OBJC_CLASS_$_Protocol;
        Class cls = (Class)&OBJC_CLASS_$_Protocol;
        ASSERT(cls);
        NXMapTable *protocol_map = protocols();
        bool isPreoptimized = hi->hasPreoptimizedProtocols();

        // Skip reading protocols if this is an image from the shared cache
        // and we support roots
        // Note, after launch we do need to walk the protocol as the protocol
        // in the shared cache is marked with isCanonical() and that may not
        // be true if some non-shared cache binary was chosen as the canonical
        // definition
        if (launchTime && isPreoptimized && cacheSupportsProtocolRoots) {
            if (PrintProtocols) {
                _objc_inform("PROTOCOLS: Skipping reading protocols in image: %s",
                             hi->fname());
            }
            continue;
        }

        bool isBundle = hi->isBundle();
        // 获取到Mach-O中的静态段__objc_protolist协议列表,存入protocol_map表
        protocol_t * const *protolist = _getObjc2ProtocolList(hi, &count);
        for (i = 0; i < count; i++) {
            readProtocol(protolist[i], cls, protocol_map, 
                         isPreoptimized, isBundle);
        }
    }
... 
    // Fix up @protocol references
    // Preoptimized images may have the right 
    // answer already but we don't know for sure.
    //7、修复没有被加载的协议
    for (EACH_HEADER) {
        // At launch time, we know preoptimized image refs are pointing at the
        // shared cache definition of a protocol.  We can skip the check on
        // launch, but have to visit @protocol refs for shared cache images
        // loaded later.
        if (launchTime && cacheSupportsProtocolRoots && hi->isPreoptimized())
            continue;
        // 获取到Mach-O的静态段 __objc_protorefs
        protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
        for (i = 0; i < count; i++) {
            // 比较协议内存地址是否相同,不同则替换
            remapProtocolRef(&protolist[i]);
        }
    }
...
    // 8、分类处理
    // Discover categories. Only do this after the initial category
    // attachment has been done. For categories present at startup,
    // discovery is deferred until the first load_images call after
    // the call to _dyld_objc_notify_register completes. rdar://problem/53119145
    // 需要在分类初始化将数据加载到类后才执行,对于运行时出现的分类,将分类的发现推迟到对_dyld_objc_notify_register调用完成后地方第一个load_images调用为止
    if (didInitialAttachCategories) {
        for (EACH_HEADER) {
            load_categories_nolock(hi);
        }
    }
...
    // Category discovery MUST BE Late to avoid potential races
    // when other threads call the new category code before
    // this thread finishes its fixups.

    // +load handled by prepare_load_methods()

    // Realize non-lazy classes (for +load methods and static instances)
    // 9、类的加载处理
    for (EACH_HEADER) {
        // 获取Mach-O的静态段__objc_nlclslist非懒加载类表
        classref_t const *classlist =
            _getObjc2NonlazyClassList(hi, &count);
        for (i = 0; i < count; i++) {
            Class cls = remapClass(classlist[i]);
            if (!cls) continue;
            // 插入当前类
            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
            }
            // 实现类上部分的cls为类名+地址但是类数据并没初始化。
            realizeClassWithoutSwift(cls, nil);
        }
    }
...
    // 10、没有被处理的类,优化那些被侵犯的类
    // 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, nil);
            cls->setInstancesRequireRawIsaRecursively(false/*inherited*/);
        }
        free(resolvedFutureClasses);
    }

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

    if (DebugNonFragileIvars) {
        // 实现所有类
        realizeAllClasses();
    }
...
}

第三步的readClass方法,走过之后打印

readClass打印.png

看下源码:

/***********************************************************************
* 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();
    //当前类的父类中若有丢失的weak-linked类,则返回nil
    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;
    }
    
    cls->fixupBackwardDeployingStableSwift();

    Class replacing = nil;
    // 判断是否是未来要处理的类(断点后发现不会走)
    if (Class newCls = popFutureNamedClass(mangledName)) {
    ... ro rw处理
}
    // 判断类是否已经加载到内存
    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);        // 插入表  machO-->内存
    }

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

mangledName方法源码:

    const char *mangledName() { 
        // fixme can't assert locks here
        ASSERT(this);
        // 已经实现或者未来类,类名从rw->ro()->name
        if (isRealized()  ||  isFuture()) {
            return data()->ro()->name;
        } else {
            //从ro中获取
            return ((const class_ro_t *)data())->name;
        }
    }

addNamedClass源码分析:

/***********************************************************************
* addNamedClass
* Adds name => cls to the named non-meta class map.
* Warns about duplicate class names and keeps the old mapping.
* Locking: runtimeLock must be held by the caller
**********************************************************************/
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 {
        // 插入到gdb_objc_realized_classes哈希表 在runtimeinit()方法中创建
        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());
}

获取到类名之后把类名插入gdb_objc_realized_classes哈希表。

gdb_objc_realized_classes.png

readClass总结: 只是把类从machO取出读到内存,插入表中,获取到的类为地址 + 名字

realizeClassWithoutSwift 方法

realizeClassWithoutSwift主要是下面三个步骤:
1.处理data(),将data()---ro替换为rw,并把ro拷贝到rw -->ro
2.递归调用realizeClassWithoutSwift完善类结构和继承连关系。
3.methodizeClass处理方法。

1 处理data()数据

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

    class_rw_t *rw;
    Class supercls;
    Class metacls;

    if (!cls) return nil;
    if (cls->isRealized()) return cls; // 如果是已实现类(data()读出来是rw) 直接返回
    ASSERT(cls == remapClass(cls));

    // fixme verify class is not in an un-dlopened part of the shared cache?
    // 取出类data()此时为class_ro_t
    auto ro = (const class_ro_t *)cls->data();
    auto isMeta = ro->flags & RO_META; //是否是元类
    if (ro->flags & RO_FUTURE) {  // 是否是未来类
        // This was a future class. rw data is already allocated.
        rw = cls->data();
        ro = cls->data()->ro();
        ASSERT(!isMeta);
        cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
    } else {
        // Normal class. Allocate writeable class data.
        rw = objc::zalloc(); //rw分配空间
        rw->set_ro(ro); //设置rw中的ro
        rw->flags = RW_REALIZED|RW_REALIZING|isMeta; // 设置flag  RW_REALIZED class_t->data 是rw非ro  RW_REALIZING 类已经实现但未完成
        cls->setData(rw); //data()设置为rw
    }
...
  • roreadOnly在编译时确定了内存,包含类名、方法、协议、和实例变量等信息,由于是只读的,被称为clean Memory,值加载后不会发生改变的内存。
  • rw表示readWrite,由于动态性,可能会往类中添加属性,方法、协议,在2020WWDC中关于内存优化提到rw,只有10%的类改变了rw,所以有的rwe,类的额外可读可写结构,rw为可读可写,所有为dirty Memory,运行时可能会添加方法或者属性等。

2.递归调用 realizeClassWithoutSwift 完善类结构和继承连关系。

// 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.
    // 递归调用 确定继承连关系 实现父类和元类  remapClass中当找到NSObject返回nil打破循环
    supercls = realizeClassWithoutSwift(remapClass(cls->superclass), nil);
    // 根元类isa指向自身 通过if (cls->isRealized()) return cls 打破循环。
    metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
    ...
        // 完善类结构 赋值父类和isa指向元类
    cls->superclass = supercls;
    cls->initClassIsa(metacls);
    ....
        // Connect this class to its superclass's subclass lists
    if (supercls) {
        // 双向绑定父类子类关系
        addSubclass(supercls, cls);
    } else {
        // 绑定NSObjcet或者别的没有父类的根类,父类指向nil
        addRootClass(cls);
    }
    ....

递归调用realizeClassWithoutSwift 循环设置父类元类类,经测试最先走的类是NSObject元类,然后是NSObject类。

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

    if (!cls) return nil;

    auto *map = remappedClasses(NO);
    if (!map)
        return cls;
    
    auto iterator = map->find(cls);
    if (iterator == map->end())
        return cls;
    return std::get<1>(*iterator);
}

3.methodizeClass

/***********************************************************************
* 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, Class previously)
{
    runtimeLock.assertLocked();

    bool isMeta = cls->isMetaClass();
    auto rw = cls->data();
    auto ro = rw->ro();
    auto rwe = rw->ext();
    
    // 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.
    // 添加方法列表、属性列表、协议列表到rw中
    // 方法列表
    method_list_t *list = ro->baseMethods();
    if (list) {
        prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls)); //根据sel地址,对方法列表排序
        if (rwe) rwe->methods.attachLists(&list, 1);
    }
    // 属性列表
    property_list_t *proplist = ro->baseProperties;
    if (rwe && proplist) {
        rwe->properties.attachLists(&proplist, 1);
    }
    // 协议列表
    protocol_list_t *protolist = ro->baseProtocols;
    if (rwe && protolist) {
        rwe->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 根元类添加initialize方法
        addMethod(cls, @selector(initialize), (IMP)&objc_noop_imp, "", NO);
    }

    // Attach categories.加入分类方法
    if (previously) {
        if (isMeta) {
            objc::unattachedCategories.attachToClass(cls, previously,
                                                     ATTACH_METACLASS);
        } else {
            // When a class relocates, categories with class methods
            // may be registered on the class itself rather than on
            // the metaclass. Tell attachToClass to look for those.
            objc::unattachedCategories.attachToClass(cls, previously,
                                                     ATTACH_CLASS_AND_METACLASS);
        }
    }
    objc::unattachedCategories.attachToClass(cls, cls,
                                             isMeta ? ATTACH_METACLASS : ATTACH_CLASS);
...
}

methodizeClass方法主要是为添加方法、属性、协议,当当前调用者为类的时候,只会对rw->ro()的方法进行排序(按照sel地址),此时取出的是ro,不是rwe

attachToClass方法

    void attachToClass(Class cls, Class previously, int flags)
    {
        runtimeLock.assertLocked();
        ASSERT((flags & ATTACH_CLASS) ||
               (flags & ATTACH_METACLASS) ||
               (flags & ATTACH_CLASS_AND_METACLASS));

        auto &map = get();
        auto it = map.find(previously);

        if (it != map.end()) {
            category_list &list = it->second;
            if (flags & ATTACH_CLASS_AND_METACLASS) {
                int otherFlags = flags & ~ATTACH_CLASS_AND_METACLASS;
                // 实例方法
                attachCategories(cls, list.array(), list.count(), otherFlags | ATTACH_CLASS);
                // 类方法
                attachCategories(cls->ISA(), list.array(), list.count(), otherFlags | ATTACH_METACLASS);
            } else {
                attachCategories(cls, list.array(), list.count(), flags);
            }
            map.erase(it);
        }
    }

核心方法是attachCategories,添加分类信息,分为+-方法。

// Attach method lists and properties and protocols from categories to a class.
// Assumes the categories in cats are all loaded and sorted by load order, 
// oldest categories first.
static void
attachCategories(Class cls, const locstamped_category_t *cats_list, uint32_t cats_count,
                 int flags)
{
    if (slowpath(PrintReplacedMethods)) {
        printReplacements(cls, cats_list, cats_count);
    }
    if (slowpath(PrintConnecting)) {
        _objc_inform("CLASS: attaching %d categories to%s class '%s'%s",
                     cats_count, (flags & ATTACH_EXISTING) ? " existing" : "",
                     cls->nameForLogging(), (flags & ATTACH_METACLASS) ? " (meta)" : "");
    }

    /*
     * Only a few classes have more than 64 categories during launch.
     * This uses a little stack, and avoids malloc.
     *
     * Categories must be added in the proper order, which is back
     * to front. To do that with the chunking, we iterate cats_list
     * from front to back, build up the local buffers backwards,
     * and call attachLists on the chunks. attachLists prepends the
     * lists, so the final result is in the expected order.
     */
    constexpr uint32_t ATTACH_BUFSIZ = 64;
    // 二维数组
    method_list_t   *mlists[ATTACH_BUFSIZ];
    property_list_t *proplists[ATTACH_BUFSIZ];
    protocol_list_t *protolists[ATTACH_BUFSIZ];

    uint32_t mcount = 0;
    uint32_t propcount = 0;
    uint32_t protocount = 0;
    bool fromBundle = NO;
    bool isMeta = (flags & ATTACH_METACLASS);
    // ⚠️ 如果rew没有,初始化rwe 并把ro中的方法、属性、协议列表储存到rwe中
    auto rwe = cls->data()->extAllocIfNeeded();

    for (uint32_t i = 0; i < cats_count; i++) {
        auto& entry = cats_list[i];
        method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
        if (mlist) {
            if (mcount == ATTACH_BUFSIZ) {
                prepareMethodLists(cls, mlists, mcount, NO, fromBundle); //排序
                rwe->methods.attachLists(mlists, mcount);
                mcount = 0;
            }
            //倒叙插入
            mlists[ATTACH_BUFSIZ - ++mcount] = mlist;
            fromBundle |= entry.hi->isBundle();
        }

        property_list_t *proplist =
            entry.cat->propertiesForMeta(isMeta, entry.hi);
        if (proplist) {
            if (propcount == ATTACH_BUFSIZ) {
                rwe->properties.attachLists(proplists, propcount);
                propcount = 0;
            }
            proplists[ATTACH_BUFSIZ - ++propcount] = proplist;
        }

        protocol_list_t *protolist = entry.cat->protocolsForMeta(isMeta);
        if (protolist) {
            if (protocount == ATTACH_BUFSIZ) {
                rwe->protocols.attachLists(protolists, protocount);
                protocount = 0;
            }
            protolists[ATTACH_BUFSIZ - ++protocount] = protolist;
        }
    }

    if (mcount > 0) {
        prepareMethodLists(cls, mlists + ATTACH_BUFSIZ - mcount, mcount, NO, fromBundle);
        rwe->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount);
        if (flags & ATTACH_EXISTING) flushCaches(cls);
    }

    rwe->properties.attachLists(proplists + ATTACH_BUFSIZ - propcount, propcount);

    rwe->protocols.attachLists(protolists + ATTACH_BUFSIZ - protocount, protocount);
}

⚠️auto rwe = cls->data()->extAllocIfNeeded(),rwe是在这个方法初始化的,因为这里要对roclean memory进行处理了,并把ro中的方法、属性、协议列表赋值到rwe中,全局搜索cls->data()->extAllocIfNeeded()发现除了这里还有_class_addPropertyclass_addProtocoladdMethods三个方法进行调用,原理相同就是要改变ro本来的clean memory的时候才会开辟。

attachLists方法

    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 从什么位置开始拷贝什么 放多大
            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;
            // oldlist放最后
            if (oldList) array()->lists[addedCount] = oldList;
            //memcpy 从开始位置开始放入addedLists,
            memcpy(array()->lists, addedLists, 
                   addedCount * sizeof(array()->lists[0]));
        }
    }

attachLists方法主要是为methodspropertiesprotocols二维数组赋值.

  • list = addedLists[0]0-1过程。
  • 1 list -> many lists1-多过程,先将旧数组放到最后位置,然后从位置0插入新数据。
  • many lists -> many lists 为多对多过程,先将旧数组后移要添加的数量位置,然后从位置0插入新数据。
map_images流程图.png

分类加载

1.定义一个类,两个分类,有同名方法

@interface Person : NSObject
-(void)say;
@end
@implementation Person
- (void)say
{
}
//+ (void)load
//{
//    NSLog(@" %s", __func__);
//}

//////////////////////////////////////////

@interface Person (e1)
-(void)addmethod2;
-(void)addmethod1;
@end
-(void)addmethod2
{
    
}
-(void)addmethod1
{
    
}
//
//+ (void)load
//{
//    NSLog(@" %s", __func__);
//}

-(void)say
{
    NSLog(@"e1 %s", __func__);
}
//////////////////////////////////////////
@interface Person (e2)
-(void)aaaaaaa;
@end
@implementation Person (e2)
//+ (void)load
//{
//    NSLog(@" %s", __func__);
////    method_exchangeImplementations(class_getInstanceMethod(self, @selector(say)), class_getInstanceMethod(self, @selector(say2)));
//}
-(void)aaaaaaa
{
    
}
-(void)say
{
    NSLog(@"e2 %s", __func__);
}

main函数

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        Person *p =[Person alloc];
        [p say];
    }
    return 0;
}

2.运行查看堆栈信息。

  • 1.都不实现load方法
    分类分析1.png

从打印堆栈顺序可知,先执行的 main.m中的[p say],开始方法的慢速查找,realizeClassWithoutSwift实现该类,ro->baseMethods()取出的list已经包括了所有的方法,来打印下:

分类分析1方法未排序.png

包括声明的所有方法,但是是没有顺序的。往下一步排序:


分类分析1方法已排序.png

已经是排序好的方法。

  • 2.主类实现load方法,分类不实现
    分类分析2.png

从堆栈调用信息可知,此流程通过map_images->_read_images实现类,在main方法之前调用,通过刚才总结的_read_images的第九步实现类。

分类分析2.1.png

`ro->baseMethods()`取出的`list`已经包括了所有的方法,后续步骤与第一步相同,只是前面执行的时机不相同。
  • 3.主类实现load方法,分类实现load方法
    分类分析3.png

    分类分析3.1.png

    通过方法打印可知先调用的map_images->_read_images实现类,然后通过load_images->loadAllCategories-...-->attachCategories添加分类数据,在此时创建rwe,把分类方法经过排序后放进rwe->methods中,attachLists方法走一对多过程。
  • 4.主类不实现load方法,分类实现load方法
    分类分析4.png

通过堆栈打印信息可知,通过load_images->prepare_load_methods->realizeClassWithoutSwift先实现本类。

分类分析4.1.png

通过unattachedCategories.attachToClass->attachCategories添加分类方法,rweattachCategories方法中创建,attachLists添加分类方法走1 list -> many lists过程。

你可能感兴趣的:(类、分类的加载)