OC底层原理12-类加载(一)

iOS--OC底层原理文章汇总

本文介绍类的信息加载。
前面篇章中简单分析了dyld的流程,再到dylibsystem调用_objc_init,整个流程的目的是为了将类的信息加载到内存中。包括其属性、方法、协议、分类等,将代码编译,编成MachO的格式,再写入到内存。

Mach-O为Mach object文件格式的缩写,它是mac以及iOS上一种用于可执行文件、目标代码、动态库的文件格式。常见:目标文件:.o;库文件.a, .dylib,Framework;可执行文件:dyld,.dsym

_read_images

先介绍读取镜像文件,它存在于objc工程中的objc-runtime-new.mm中,代码量非常多,但我们从其中得到重要的信息,并对我们关心的类相关处理做分析。
1、条件控制进行的一次加载
2、修复预编译阶段的@selector的混乱问题
3、错误混乱的类处理
4、修复重映射一些没有被镜像文件加载进来的类
5、修复一些消息
6、当类里面有协议时:readProtocol 读取协议
7、修复没有被加载的协议
8、分类处理
9、类的加载处理
10、没有被处理的类,优化那些被侵犯的类

条件控制进行的一次加载
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;
    //创建哈希表:方便快速对类进行查找
    gdb_objc_realized_classes =
        NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
    ts.log("IMAGE TIMES: first time tasks");
}

doneOnce流程中通过NXCreateMapTable创建表,存放类信息,即创建一张类的哈希表gdb_objc_realized_classes,其目的是为了类查找方便、快捷。

修复预编译阶段的@selector的混乱问题

从从 _getObjc2SelectorRefs获得MachO中的静态段__objc_selrefs,然后遍历获取sel,修复sel不一致问题。

// Fix up @selector references
    static size_t UnfixedSelectors;
    {
        mutex_locker_t lock(selLock);
        for (EACH_HEADER) {
            if (hi->hasPreoptimizedSelectors()) continue;
            bool isBundle = hi->isBundle();
            //从 _getObjc2SelectorRefs获得MachO中的静态段__objc_selrefs
            SEL *sels = _getObjc2SelectorRefs(hi, &count);
            UnfixedSelectors += count;
            // 对列表进行遍历
            for (i = 0; i < count; i++) {
                // 获取sel字符
                const char *name = sel_cname(sels[i]);
                // 透过name获取sel,该sel类型是:(SEL)*it.first,取了地址
                SEL sel = sel_registerNameNoLock(name, isBundle);
                // 如果名字可能相同,但地址不同,就修复不相同的的sel
                if (sels[i] != sel) {
                    sels[i] = sel;
                }
            }
        }
    }

获取MachO中的静态段名

//      function name                 content type     section name
GETSECT(_getObjc2SelectorRefs,        SEL,             "__objc_selrefs"); 
GETSECT(_getObjc2MessageRefs,         message_ref_t,   "__objc_msgrefs"); 
GETSECT(_getObjc2ClassRefs,           Class,           "__objc_classrefs");
GETSECT(_getObjc2SuperRefs,           Class,           "__objc_superrefs");
GETSECT(_getObjc2ClassList,           classref_t const,      "__objc_classlist");
GETSECT(_getObjc2NonlazyClassList,    classref_t const,      "__objc_nlclslist");
GETSECT(_getObjc2CategoryList,        category_t * const,    "__objc_catlist");
GETSECT(_getObjc2CategoryList2,       category_t * const,    "__objc_catlist2");
GETSECT(_getObjc2NonlazyCategoryList, category_t * const,    "__objc_nlcatlist");
GETSECT(_getObjc2ProtocolList,        protocol_t * const,    "__objc_protolist");
GETSECT(_getObjc2ProtocolRefs,        protocol_t *,    "__objc_protorefs");
GETSECT(getLibobjcInitializers,       UnsignedInitializer, "__objc_init_func");
错误混乱的类处理
// Discover classes. Fix up unresolved future classes. Mark bundle classes.
bool hasDyldRoots = dyld_shared_cache_some_image_overridden();
for (EACH_HEADER) {
    if (! mustReadClasses(hi, hasDyldRoots)) {
        // 如果镜像已经优化,无需调用readclass()
        continue;
    }
    //获取编译后类列表中的所有类,从Mach-O获取__objc_classlist,获得一个classref_t类型的指针
    classref_t const *classlist = _getObjc2ClassList(hi, &count);
    bool headerIsBundle = hi->isBundle();
    bool headerIsPreoptimized = hi->hasPreoptimizedClasses();
    for (i = 0; i < count; i++) {
         // 从列表中得到cls,但只有地址
        Class cls = (Class)classlist[I];
        // ** 读取类,使得cls获取的值有对应的name。此时cls包含了地址+类的name
        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读取类

clsreadClass被调用之前只是从表中获取到了地址,未获取name

/* 读取阅读类
* 读取由编译器编写的类和元类
* 返回新的类指针。这可能是: 
* - cls
* - 零 (cls 缺少弱链接超类) 
* - 其他内容(此类的空间由将来的类保留)
* 请注意,此功能执行的所有工作都由
* 必读类 () 。在不更新该函数之前,请勿更改
* 锁定:由用户或map_images获取的objc_readClassPair
*/
Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
{
    // 若当前类没有父类,就返回nil
    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;
    }
    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());
        }
        // 读取newCls的data,从ro复制一份data,赋值给rw
        class_rw_t *rw = newCls->data();
        const class_ro_t *old_ro = rw->ro();
        memcpy(newCls, cls, sizeof(objc_class));
        rw->set_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);
        // 将处理后的cls插入到表中,即写到内存中
        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;
}

补充:之前篇章中objc_class中有一个bits -> class_data_bits_t,它下面有一个结构:
class_ro_t(ro): read only 它是从存储中读取数据到内存中,用来存储name、方法、协议和实例变量等;加载完成后,改数据不会发生变化又称为干净内存(clean memory)
class_rw_t (rw): read write 存储和获取。在进程运行时发生更改的内存。在数据增删改查过程中为了不对原来数据进行更改,为类在runtime过程中分配一个额外的内存,从ro中copy一份data到rw中,所以它是可变的。这个内存变成了脏内存(dirty memory)。但是在实际应用中,类的使用量只是10%,这样就在rw中造成了内存浪费,所以苹果就把rw中方法、协议和实例变量等放到了class_rw_ext_t中。
class_rw_ext_t(rwe): read write ext,在runtime过程中存储类的方法、协议和实例变量等信息。

mangledName获取cls的name

const char *mangledName() { 
        // fixme can't assert locks here
        // ASSERT(this);
        if (!this) {
            return "";
        }
        if (isRealized()  ||  isFuture()) {
            // 判断如果已经初始化or将处理的,则就从ro中读取name
            return data()->ro()->name;
        } else {
            //   从MachO的data中读取name
            return ((const class_ro_t *)data())->name;
        }
    }

addNamedClass将name、cls地址存储下来

/***********************************************************************
* addNamedClass 
* Adds name => cls to the named non-meta class map. 将name=> cls添加到命名的非元类映射
* 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;
    // 若dyld的共享缓存类中class有数据
    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.
        // 使用name查找,未按名称查找的类存在非元类表中
        addNonMetaClass(cls);
    } else {
        //如果old类信息为nil,则将cls、name添加到gdb_objc_realized_classes哈希表存储。gdb_objc_realized_classes:不在dyld共享缓存中的命名类
        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());
}

addClassTableEntry 将初始化之后的类存储到所有类的表中,如果有元类,会自动添加类的元类。

/***********************************************************************
* addClassTableEntry
* Add a class to the table of all classes. If addMeta is true,
* automatically adds the metaclass of the class as well.
* Locking: runtimeLock must be held by the caller.
**********************************************************************/
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.
    auto &set = objc::allocatedClasses.get();

    ASSERT(set.find(cls) == set.end());

    if (!isKnownClass(cls))
        set.insert(cls);
    if (addMeta)
        addClassTableEntry(cls->ISA(), false);
}

整个以上的过程为:从_read_images,到类的处理readClass,readClass中,执行addNamedClass,addClassTableEntry,就将cls的地址、name就存储到了内存中。

类的加载处理

在类文件中比如LGPerson中实现

+load
{
    // ...
}

在类处理是就可以直接进入以下代码:实现非懒加载类,

// Realize non-lazy classes (for +load methods and static instances) 
    for (EACH_HEADER) {
        //通过_getObjc2NonlazyClassList获取Mach-O的静态段__objc_nlclslist非懒加载类表
        classref_t const *classlist = 
            _getObjc2NonlazyClassList(hi, &count);
        for (i = 0; i < count; i++) {
            Class cls = remapClass(classlist[i]);
            
            const char *mangledName  = cls->mangledName();
             const char *LGPersonName = "LGPerson";
            
             if (strcmp(mangledName, LGPersonName) == 0) {
                 auto kc_ro = (const class_ro_t *)cls->data();
                 printf("_getObjc2NonlazyClassList: 这个是我要研究的 %s \n",LGPersonName);
             }
            //判断是否为空。不为空则就插入表。如果前面已经插入过了,则不会重新插入
            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
            }
            //实现加载所有非懒加载的类(实例化类对象的一些信息,例如rw)
            realizeClassWithoutSwift(cls, nil);
        }
    }
  • 1.读取一个非懒加载classlist,对其进行遍历,如果该遍历的cls已经存在表中,则继续。如果没有则添加到类表中。
  • 2.realizeClassWithoutSwift实现遍历的当前类,加载出除cls地址、name之外的data数据。

realizeClassWithoutSwift->实现类

对类cls进行首次初始化,包括分配其读写数据,返回类的真实类结构。代码量挺多的就研究分析流程了。

该方法在分析消息流程的慢速查找流程时也出现过,判断类是否初始化,如果未初始化,则初始化cls,并最后实现类。lookUpImpOrForward -> realizeClassMaybeSwiftAndLeaveLocked -> realizeClassMaybeSwiftMaybeRelock -> realizeClassWithoutSwift
OC底层原理08-objc_msgSend方法消息慢速查找(二)

  • 1.读取MachO的data
    将cls的data数据读取出来,并转换为class_ro_t *,赋值给一个ro.并且复制一份data给rw。
    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
        rw = cls->data();
        ro = cls->data()->ro();
        ASSERT(!isMeta);
        cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
    } else {
        // Normal class. Allocate writeable class data.正常类的情况就写入分配类data
        // 开票一个空间给rw。
        rw = objc::zalloc<class_rw_t>();
        //   设置rw中ro
        rw->set_ro(ro); //
        rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
        cls->setData(rw); // 把rw的数据写入 :setData(rw) -> bits.setData(newData);
    }

查看set_ro的实现,可以发现set_ro -> set_ro_or_rwe(找到 get_ro_or_rwe,是通过ro_or_rw_ext_t类型从ro_or_rw_ext中获取) -> ro_or_rw_ext_t中的ro.如果有运行时,从rw中读取;反之,如果没有运行时,从ro中读取.

  • 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.
    supercls = realizeClassWithoutSwift(remapClass(cls->superclass), nil);
    metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
    
    // Connect this class to its superclass's subclass lists
    //双向链表指向关系 父类中可以找到子类 子类中也可以找到父类
    //通过addSubclass把当前类放到父类的子类列表中去
    if (supercls) {
        addSubclass(supercls, cls);
    } else {
        addRootClass(cls);
    }
    1. setInstancesRequireRawIsa or methodizeClass
#if SUPPORT_NONPOINTER_ISA
    if (isMeta) {
        // Metaclasses do not need any features from non pointer ISA
        // This allows for a faspath for classes in objc_retain/objc_release.
        cls->setInstancesRequireRawIsa();
    } else {
        // 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  &&  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->setInstancesRequireRawIsaRecursively(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 附加类别 将ro数据中方法列表(包括分类的方法)、属性列表、协议列表都写入到rw
    methodizeClass(cls, previously);

    return cls;

在main.m文件中调用一个类的初始化方法,在此处断点调试;首次进入该段代码,会先走cls->instancesRequireRawIsa();流程,深入一下,就能找到

inline void 
objc_object::initClassIsa(Class cls)
{
    if (DisableNonpointerIsa  ||  cls->instancesRequireRawIsa()) {
        initIsa(cls, false/*not nonpointer*/, false);
    } else {
        initIsa(cls, true/*nonpointer*/, false);
    }
}

就是一个初始化isa的必要条件。
我们在这里打印一下cls,能获取到cls的信息。

(lldb) x/4gx cls
0x1000032e8: 0x00000001000032c0 0x0000000100335140
0x1000032f8: 0x000000010032f430 0x0000000000000000
// 依次是: isa ,继承,cache,bits

此时还未初始化,所以最后一个地址没有数据,是因为内存情况还未完善。但是如果这样(const class_ro_t *)cls->data();是能获取到data数据的,通过指针是可以获取到数据的。
初始化空间,设置cls数据

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

cls就有了初始值

(lldb) x/4gx cls
0x1000032e8: 0x00000001000032c0 0x0000000100335140
0x1000032f8: 0x000000010032f430 0x0000202400000000

根据数据流 : cls->data() -> ro -> rw ,rw数据里面的rwe是在methodizeClass中处理的。

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();  // rw中的ext 数据赋值给rwe

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

#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
}
  • 在该方法中,进行了ro、rw、rwe初始赋值后。从ro总读取到方法列表,将方法列表转换为method_list_t类型的list;
  • 对方法列表进行排序prepareMethodLists(属性、方法、协议表)。
static void 
prepareMethodLists(Class cls, method_list_t **addedLists, int addedCount,
                   bool baseMethods, bool methodsFromBundle)
{
    // ...
    // Add method lists to array.
    // Reallocate un-fixed method lists.
    // The new methods are PREPENDED to the method list array.
    for (int i = 0; i < addedCount; i++) {
        method_list_t *mlist = addedLists[i];
        ASSERT(mlist);
        // Fixup selectors if necessary
        if (!mlist->isFixedUp()) {
            fixupMethodList(mlist, methodsFromBundle, true/*sort*/);//排序
        }
    }
    // ...
}
// ----------------------
static void 
fixupMethodList(method_list_t *mlist, bool bundleCopy, bool sort)
{
    runtimeLock.assertLocked();
    ASSERT(!mlist->isFixedUp());

    // fixme lock less in attachMethodLists ?
    // dyld3 may have already uniqued, but not sorted, the list
    if (!mlist->isUniqued()) {
        mutex_locker_t lock(selLock);
    
        // Unique selectors in list.
        for (auto& meth : *mlist) {
            const char *name = sel_cname(meth.name);
            meth.name = sel_registerNameNoLock(name, bundleCopy);
        }
    }

    // Sort by selector address.根据sel地址排序
    if (sort) {
        method_t::SortBySELAddress sorter;
        std::stable_sort(mlist->begin(), mlist->end(), sorter);
    }
    
    // Mark method list as uniqued and sorted
    mlist->setFixedUp();
}

方法的排序原则是:根据sel地址排序

  • 序列化各list之后,对rwe进行了赋值,此时rwe就有了值:rwe->methods.attachLists
void attachToClass(Class cls, Class previously, int flags)
{
    runtimeLock.assertLocked();
    ASSERT((flags & ATTACH_CLASS) ||
           (flags & ATTACH_METACLASS) ||
           (flags & ATTACH_CLASS_AND_METACLASS));

    
    const char *mangledName  = cls->mangledName();
    const char *LGPersonName = "LGPerson";

    if (strcmp(mangledName, LGPersonName) == 0) {
        bool kc_isMeta = cls->isMetaClass();
        auto kc_rw = cls->data();
        auto kc_ro = kc_rw->ro();
        if (!kc_isMeta) {
            printf("%s: 这个是我要研究的 %s \n",__func__,LGPersonName);
        }
    }
    auto &map = get();
    auto it = map.find(previously);//找到一个分类进来一次,即一个个加载分类,不要混乱
    if (it != map.end()) {//这里会走进来:当主类没有实现load,分类开始加载,迫使主类加载,会走到if流程里面
        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
            attachCategories(cls, list.array(), list.count(), flags);
        }
        map.erase(it);
    }
}

category_list -> attachCategories中有了对分类的操作处理,那么就需要对分类是如何加载再做一个探究—类加载(二)。

懒加载类&非懒加载类

区别:当前类是否实现load方法;不实现它就是懒加载,实现了+load则是非懒加载。

  • 1.懒加载类情况
    把数据加载推迟到第一次消息
    lookUpImpOrForward
    realizeClassMaybeSwiftMaybeRelock
    realizeClassWithoutSwift
    methodizeClass

  • 2.非懒加载类情况
    实现load方法,在map_images的时候,就加载所有类数据
    _getObjc2NonlazyClassList
    readClass
    realizeClassWithoutSwift
    methodizeClass
    苹果方默认为了性能是采取懒加载类。

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