OC底层原理12之类的加载

1,583 阅读11分钟

本章内容

  1. 类的实现源码解析
  2. 懒加载类与非懒加载类
  3. 对于类结构中的rw,ro,rwe的分析

本章目的

知道类什么时候加载的,对于我们的启动优化也有帮助,知道rw,ro,rwe的区别和诞生的原因,以及rwe何时赋值,对于了解运行时机制有帮助。对于类的加载,我们本文说的其实是类的实现(说类的加载也没错),为什么这么说后面会讲。

类的实现

我们都知道我们OC/(Swift,swift本身是没有runtime的,但他们都共用了objc,只是swift的类和OC实现不同。)与其他语言不同是因为在运行时我们可以改变类的结构,内存大小,把需要在编译时进行的放在了运行时等等。但是一门编程语言与其他语言不同但肯定也是有相同处,就是我们在编译时已经能确定的东西,例如类的成员变量,名字等等这些东西。 而对于类的实现我们还要借助 objc初始化所看到的关于类的实现所用到的方法分析。

提示:如果说,你在看这个小节对ro,rw,rwe有疑问,可以先看我下面的解释再看本小节

readClass 分析

在上一章的时候就你就可以看到,我对于read_images的描述中3流程能大致知道该函数里,关于对类的处理做了哪些东西。就是从mach-o里面读取类列表,然后一个个去给类添加名字,并加入我们之前提到过的全局类表中。这个操作无论系统的类还是我们自己创建的都会经历

Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
{
    // 非懒加载类获取类名的方式,但是其实这里已经都能获取了,可以看里面实现方式是从ro读取的
    const char *mangledName = cls->nonlazyMangledName();
    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->setSuperclass(nil);
        return nil;
    }
    
    cls->fixupBackwardDeployingStableSwift();
    
    Class replacing = nil;
    if (mangledName != nullptr) {
    // 在这里千万不要以为类实现中的ro赋值于rw的操作是在这。
    // 可以自行验证,你会发现无论是系统还是自己创建的都没有走这里
        if (Class newCls = popFutureNamedClass(mangledName)) {
            // This name was previously allocated as a future class.
            // Copy objc_class to future class's struct.
            // Preserve future's rw data block.

            if (newCls->isAnySwift()) {
                _objc_fatal("Can't complete future class request for '%s' "
                            "because the real class is too big.",
                            cls->nameForLogging());
            }

            class_rw_t *rw = newCls->data();
            const class_ro_t *old_ro = rw->ro();
            memcpy(newCls, cls, sizeof(objc_class));

            // Manually set address-discriminated ptrauthed fields
            // so that newCls gets the correct signatures.
            newCls->setSuperclass(cls->getSuperclass());
            newCls->initIsa(cls->getIsa());

            rw->set_ro((class_ro_t *)newCls->data());
            newCls->setData(rw);
            freeIfMutable((char *)old_ro->getName());
            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(mangledName == nullptr || getClassExceptSomeSwift(mangledName));
    } else {
        if (mangledName) { //some Swift generic classes can lazily generate their names
            // 将类地址与名字绑定起来
            addNamedClass(cls, mangledName, replacing);
        } else {
            Class meta = cls->ISA();
            const class_ro_t *metaRO = meta->bits.safe_ro();
            ASSERT(metaRO->getNonMetaclass() && "Metaclass with lazy name must have a pointer to the corresponding nonmetaclass.");
            ASSERT(metaRO->getNonMetaclass() == cls && "Metaclass nonmetaclass pointer must equal the original class.");
        }
        // 将绑定后的类加到全局大类表中
        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;
}

realizeClassWithoutSwift 类实现核心方法

这个方法就是对于类实现的核心方法了,任何类(懒加载类,非懒加载类)都要经过这个方法的洗礼,才能实现。走到这里,对于类的加载你可能仍然模糊,觉得没有一个确切的答案,不要着急,先看源码,分析。源码建议仔细看一遍,虽说不一定非要记住,但能知道大概做了些什么就很好了。

其中最关键的就是对rw的赋值等等,以及本类实现,连带着元类,父类,父类元类等都实现

static Class realizeClassWithoutSwift(Class cls, Class previously)
{
    runtimeLock.assertLocked();
    
    class_rw_t *rw;
    Class supercls;
    Class metacls;

    if (!cls) return nil;
    // 如果类已经实现就返回,证明流程错误,里面有个方法会进行assert
    if (cls->isRealized()) {
        validateAlreadyRealizedClass(cls);
        return cls;
    }
    ASSERT(cls == remapClass(cls));

    // fixme verify class is not in an un-dlopened part of the shared cache?
    // 获取ro,这里的代表的是指针转换,你可以当做是强制类型。毕竟从mach-o里面读取的就是一串数字而已。
    // 但是这里你可能会有疑问,ro = rw,结构体强转得保证类型一致才行。其实你不必担心这个,因为从ro 就是从磁盘中获取的
    auto ro = (const class_ro_t *)cls->data();
    // 看看是不是元类
    auto isMeta = ro->flags & RO_META;    
    // 如果是futer class,就是在read_images方法的时候那些混乱类
    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开辟
        rw = objc::zalloc<class_rw_t>();
        // ro 赋值于rw真正地方
        rw->set_ro(ro);
        // 标识类实现
        rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
        // 不用说也知道什么意思
        cls->setData(rw);
    }
    // 这是类的缓存初始化,就是消息查找的时候那个cache_t
    cls->cache.initializeToEmptyOrPreoptimizedInDisguise();

#if FAST_CACHE_META
    if (isMeta) cls->cache.setBit(FAST_CACHE_META);
#endif

    // 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->getSuperclass()), nil);
    // 去实现它的元类
    metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);

// nonpointer_isa,不用关心
#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.
        // 我们看到元类的isa打印是其类的名字,因为这里
        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;
        //如果说你记得上一章我说的环境变量,OBJC_DISABLE_NONPOINTER_ISA
        // 所以nonpointer_isa标识就是末尾为1,下面只是去设置
        if (DisableNonpointerIsa) {
            // Non-pointer isa disabled by environment or app SDK version
            instancesRequireRawIsa = true;
        }
        else if (!hackedDispatch  &&  0 == strcmp(ro->getName(), "OS_object"))
        {
            // hack for libdispatch et al - isa also acts as vtable pointer
            hackedDispatch = true;
            instancesRequireRawIsa = true;
        }
        else if (supercls  &&  supercls->getSuperclass()  &&
                 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
    // 设置其父类,以及isa指向元类
    cls->setSuperclass(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
    // 看苹果自身备注,就知道是与分类相关,但真的是这样吗?请看下面分析
    // 不要关心参数previously,其实它可以当做是备用参数,对我们来讲无实际意义
    methodizeClass(cls, previously);

    return cls;
}

methodizeClass 分析

请记得previously这个参数无意义,请不要关心这个参数。我们在之前的方法已经对类的rw做了赋值操作,那么下面这个方法就是对类的协议,属性,方法等等做处理,至于做什么处理请看源码。

其实这个方法我们可以看出除了对方法的排序,主要是针对rwe的情况进行处理,而rwe究竟是什么,它是何时被创建,创建的条件是什么。其实从这个方法我们也能猜到一些,比方说属性,方法,协议等我们做了什么操作导致它会被创建,当然也包括了分类

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.
    // 从ro 获取方法,这些方法是在编译时就已经确定的。
    method_list_t *list = ro->baseMethods();
    if (list) {
        //对类的方法列表进行一些处理,如排序
        prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls), nullptr);
        // 当有rwe的时候去添加。
        // 额外提示,list其实存放的是一个个指针,你可以认为是数组指针,它是多维的.
        // 但是一般情况下都是一维数组指针,出现多维的原因是因为load方法。这里我不做过多解释,可以看我的分类加载的文章
        if (rwe) rwe->methods.attachLists(&list, 1);
    }
    // 跟方法处理一致,如果说rwe被染指了就会进行一些操作
    property_list_t *proplist = ro->baseProperties;
    if (rwe && proplist) {
        rwe->properties.attachLists(&proplist, 1);
    }
    // 跟方法处理一致,如果说rwe被染指了就会进行一些操作
    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.
    // previously为nil,所以不必纠结
    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);
        }
    }
    // 这个方法就有意思了,不知道是否记得runtime_init创建的两张表,其中一个。
    // objc::unattachedCategories.init(32); 表1
    // objc::allocatedClasses.init();表2
    // 在分类加载的时候我会说明,我们看到这里就可以知道,类加载和分类加载也是有很多联系的
    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
}

总结

看了类的实现,我相信你会大致知道类被加载的时候,系统帮我们做了哪些操作,比如rw的赋值,对于类的方法,属性,协议,分类等处理。但是你肯定会有疑惑,类是在何时进行加载的,它的加载与分类有什么关系,分类加载又是如何。上面我没做说明,先往下看

懒加载类与非懒加载类

为什么会有如此区分,什么是非懒加载类,什么是懒加载类。我直接先说出答案

懒加载类:类第一次触发消息发送的时候进行实现的。在消息慢速发送流程中你是否发现了realizeAndInitializeIfNeeded_locked这样一个方法。它就是去实现类的。

image.png

非懒加载类:类或者其分类实现load方法,它会在pre-main前就进行实现。也就是在map_images函数流程。

image.png

总结

其实对于类加载,在这时候说有点早,因为我们只研究了对于类的,并没有考虑分类情况。因为分类也会影响类的实现。但是虽然不完整,先做个小总结:

类在编译时就已经确定了其ro,这里的ro你可以当做类加载,但是类真正实现也就是加载到内存被我们所使用,是上面两种情况。load方法的时候是pre-main之前就进行加载并且包含其父类,元类等都被加载。当没有实现load方法的时候就是在消息第一次发送的时候加载。

为什么这么做?不知道你是否看过我之前的文章对于应用程序加载,如果说一开始就加载那些不必须的东西就会造成启动缓慢,所以对于程序加载都是懒加载方式,用一块加载一块,这样既省内存又优化了启动时间

rw, ro, rwe

这三个结构体是影响类的核心所在。

rw本质class_rw_t,dirtyMemory,它包含了ro与rwe,也就是说,我们平时所用到的方法,协议,属性等都是从这里取出来的,类实现的时候默认是ro数据,要记得rw开辟的时候赋值操作。但是为什么说包含rwe呢?

ro本质class_ro_t,cleanMemory,这是类的干净的东西,其编译时就能确定的,例如类名,成员变量,乃至一些方法、属性等,这些存在磁盘中,所以我们看上面篇章的时候类实现都是先从ro入手就是因为这些不可变,存在磁盘。

rwe本质class_rw_ext_t相当于rw的扩展,因为运行时机制我们可以动态添加方法、属性、协议等以及分类操作都会影响类的结构也就是rw,所以在有这些操作的时候系统就会进行开辟rwe,进行创建。但是又不是每一个类都会有上面操作,所以这些类就不会去开辟rwe,避免去染指类的结构

问题

注册的类能利用运行时添加成员变量吗?没有注册的类呢?

什么是已注册的类,就是已经添加到类表中,就是大类表。注册的类是不能添加的,没有注册的不用想我们都可以添加。为什么呢?我们是怎么注册类的?是通过objc_registerClassPair方法进行的,这个方法里面有个标识符RW_CONSTRUCTING,代表了类正在建设,而我们添加成员变量是怎么弄的?是通过方法class_addIvar,看其源码就知道已经建设完成的类不能添加

image.png