底层原理 -14-类的加载(中)

229 阅读11分钟

继续上篇探索类加载,之前我们进入了_read_images,我们大概看了下主要做了什么,其中实现oc的类是方法realizeClassWithoutSwift,继续下面的探索。

1.类的加载流程分析

image.png 首先我们想要断点进入我们自己的类,进行查看类的加载。但是LGPerson这个类都已经调用了方法,说明已经加载过类的信息了,还没有进入断点。上面的注释说明这是一个非懒加载实现类,我们知道在dyld流程中。load_images时会便利类的+load()方法,有的话实现这个方法。而实现类的方法必然要实现这个类。所以会进入非懒加载的实现类的流程。 image.png 进入了,说明调用类的load的方法会进入realizeClassWithoutSwift

1.1realizeClassWithoutSwift分析

/***********************************************************************
* realizeClassWithoutSwift
* Performs first-time initialization on class cls, 
* including allocating its read-write data.
* Does not perform any Swift-side initialization.
* Returns the real class structure for the class. 
* Locking: runtimeLock must be write-locked by the caller
 对类cls执行首次初始化,包括分配其读写数据,不执行任何swift端初始化,runtimeLock必须被调用者写锁定
**********************************************************************/
static Class realizeClassWithoutSwift(Class cls, Class previously)
{
    runtimeLock.assertLocked();

    class_rw_t *rw;
    Class supercls;
    Class metacls;

    if (!cls) return nil;
    if (cls->isRealized()) {
        validateAlreadyRealizedClass(cls);//实现了话直接返回cls
        return cls;
    }
    ASSERT(cls == remapClass(cls));
    

    // fixme verify class is not in an un-dlopened part of the shared cache?
    //修复验证类不在共享缓存的未打开部分
//    const char *

//    const char * systemClassName = []
    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.
        //正常的类。分配可写的类数据。
        rw = objc::zalloc<class_rw_t>();
        rw->set_ro(ro);
        rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
        cls->setData(rw);
    }
    const char *systemClassName = cls ->nonlazyMangledName();
    const char *LGPersonName = "LGPerson";
    if (strcasecmp(systemClassName, LGPersonName) == 0) {
        if (!isMeta) {
            printf("自己创建的类的方法:%s-%s",__func__,systemClassName);
        }
    }



    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->instancesRequireRawIsa
    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.这需要在上面设置rw_realize之后完成,对于根类。
    // 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.假设这些类都没有Swift内容,或者Swift的初始化器已经被调用。
    //   fixme that assumption will be wrong if we add support
    //   for ObjC subclasses of Swift classes. 自我修复如果我们添加支持,假设将是错误的用于Swift类的ObjC子类。
    supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);//实现父类链
    metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);//实现元类链关联,也就是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.
        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//不是纯的isa
            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
    //更新父类和元类,以防重新映射
    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 排序和添加分类
    methodizeClass(cls, previously);

    return cls;
}
  1. 我们首先判断这个类是否加载过了,加载过就直接返回。
  2. 读取类的数据data编译时确定的数据,赋值给ro,之后把ro复制一份给rwro在之后运行中不在改变rw数据则可以读写,如果需要新增,动态添加则写在rwe中。
  3. 初始化类的缓存
  4. 设置类的索引,确定自身的父类,和元类也就是isa指向,递归完成父类链isa走位
  5. 更新父类和元类,以防重新映射
  6. 给类的方法和属性,协议等地址排序添加分类categories。

1.2 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.安装类自身实现的方法和属性。
    method_list_t *list = ro->baseMethods();
    if (list) {
        prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls), nullptr);
        if (rwe) rwe->methods.attachLists(&list, 1);
    }

    property_list_t *proplist = ro->baseProperties;
    if (rwe && proplist) {
        //给rwe添加属性列表
        rwe->properties.attachLists(&proplist, 1);
    }

    protocol_list_t *protolist = ro->baseProtocols;
    if (rwe && protolist) {
        //给rwe添加协议列表
        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
}

  • prepareMethodLists方法列表排序,为了查找方法的时候使用二分法根据地址大小进行查找。
  • rwe的话给rwe添加未动态添加的方法列表,属性列表,协议列表。
  • 有分类的话添加分类

1.2.1 prepareMethodLists验证

static void 
prepareMethodLists(Class cls, method_list_t **addedLists, int addedCount,
                   bool baseMethods, bool methodsFromBundle, const char *why)
{
    runtimeLock.assertLocked();

    if (addedCount == 0) return;//添加的列表不能为空

    // There exist RR/AWZ/Core special cases for some class's base methods.
    // But this code should never need to scan base methods for RR/AWZ/Core:
    // default RR/AWZ/Core cannot be set before setInitialized().
    // Therefore we need not handle any special cases here.
    if (baseMethods) {
        ASSERT(cls->hasCustomAWZ() && cls->hasCustomRR() && cls->hasCustomCore());
    } else if (cls->cache.isConstantOptimizedCache()) {
        cls->setDisallowPreoptCachesRecursively(why);
    } else if (cls->allowsPreoptInlinedSels()) {
#if CONFIG_USE_PREOPT_CACHES
        SEL *sels = (SEL *)objc_opt_offsets[OBJC_OPT_INLINED_METHODS_START];
        SEL *sels_end = (SEL *)objc_opt_offsets[OBJC_OPT_INLINED_METHODS_END];
        if (method_lists_contains_any(addedLists, addedLists + addedCount, sels, sels_end - sels)) {
            cls->setDisallowPreoptInlinedSelsRecursively(why);
        }
#endif
    }

    // Add method lists to array.
    // Reallocate un-fixed method lists.重新分配未固定的方法列表。
    // The new methods are PREPENDED to the method list array.
    auto ro = (const class_ro_t *)cls->data();//自定义
    auto isMeta = ro->flags & RO_META;//自定义
    for (int i = 0; i < addedCount; i++) {
        method_list_t *mlist = addedLists[i];
        ASSERT(mlist);
        //自定义
        const char *systemClassName = cls ->nonlazyMangledName();
        const char *LGPersonName = "LGPerson";
        if (strcasecmp(systemClassName, LGPersonName) == 0) {
            if (!isMeta) {
                printf("自己创建的类的方法:%s-%s",__func__,systemClassName);
                for (auto& meth : *mlist) {
                    const char *name = sel_cname(meth.name());
                    printf("排序前%s-%p\n",name,meth.name());
                }
            }
        }

       
       
        // Fixup selectors if necessary 排序
        if (!mlist->isFixedUp()) {
            fixupMethodList(mlist, methodsFromBundle, true/*sort*/);
        }
        //自定义
        if (strcasecmp(systemClassName, LGPersonName) == 0) {
            if (!isMeta) {
                printf("自己创建的类的方法:%s-%s",__func__,systemClassName);
                for (auto& meth : *mlist) {
                    const char *name = sel_cname(meth.name());
                    printf("排序后%s-%p\n",name,meth.name());
                }
            }
        }
      
    }

    // If the class is initialized, then scan for method implementations如果类已初始化,则扫描方法实现
    // tracked by the class's flags. If it's not initialized yet,由类的标志跟踪。如果它还没有初始化,
    // then objc_class::setInitialized() will take care of it.然后objc_class::setInitialized()会处理它。
    if (cls->isInitialized()) {
        objc::AWZScanner::scanAddedMethodLists(cls, addedLists, addedCount);
        objc::RRScanner::scanAddedMethodLists(cls, addedLists, addedCount);
        objc::CoreScanner::scanAddedMethodLists(cls, addedLists, addedCount);
    }
}

其中我们在方法列表排序前打印下,排序后打印下看下结果

image.png 排序前按我们开发时顺序打印,排序后按方法地址的高低排序。继续看下排序的方法

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.setName(sel_registerNameNoLock(name, bundleCopy));
        }
    }
    

    // Sort by selector address.
    // Don't try to sort small lists, as they're immutable.
    // Don't try to sort big lists of nonstandard size, as stable_sort
    // won't copy the entries properly.
    if (sort && !mlist->isSmallList() && mlist->entsize() == method_t::bigSize) {
        method_t::SortBySELAddress sorter;
        std::stable_sort(&mlist->begin()->big(), &mlist->end()->big(), sorter);
    }
    
    // Mark method list as uniqued and sorted.
    // Can't mark small lists, since they're immutable.
    if (!mlist->isSmallList()) {
        mlist->setFixedUp();
    }
}

通过方法的地址进行排序的。

2.懒加载和非懒加载分析

2.1 懒加载(消息发送)

之前我们在进行判断当前类的时候,在main函数都执行了。还没有记载类的信息,后面通过搜索和注释发现类的+load方法会在load_images中实现prepare_load_methods方法继续实现realizeClassWithoutSwift就是文章一开始分析的。那么我们开发中,不实现类的+load方法,那它是怎样实现的呢?

image.png 直接打断点,查看它的堆栈信息。进入main函数后,先打印了hello world调用LGPerson初始化后进入实现类。堆栈已经很明显的打印了方法的调用流程。lookUpImpOrForward-->initializeAndMaybeRelock-->realizeClassMaybeSwiftMaybeRelock-->realizeClassWithoutSwift 我们平常项目有很多类,但是不是第一时间使用到,为了节约内存,也为了加快程序启动速度。使用了懒加载,当这个OC类第一次去发送消息时候,进行慢速查找,没有实现这个类的话进入realizeClassWithoutSwift实现这个类的相关信息。

2.2 非懒加载(+load)

实现+load方法走的是非懒加载流程

image.png 在dyld中运行初始化程序时进入objc_init,调用注册函数_dyld_objc_notify_register。其中会调用map_imagesload_images.正常情况不实现+load方法,map_images不会实现realizeClassWithoutSwift。load_images会递归查找类和分类的+load方法,有的话就要实现类的数据,会进入map_images-->map_images_nolock-->_read_images-->realizeClassWithoutSwift

未命名文件-2.jpg

3.分类探索

分类也是日常开发常用的,那么它是怎么加载呢?先看下本质。我们创建一个LG Person的分类A

#import "LGPerson+A.h"

@implementation LGPerson (A)
-(void)sleep{

    NSLog(@"%s",__func__);

}
-(void)eat{

    NSLog(@"%s",__func__);

}
-(void)work{

    NSLog(@"%s",__func__);
}

@end

我们用Clang编译器编译下clang -rewrite-objc LGPerson+A.m -o A.cpp得到

image.png 分类本质也是结构体 搜索_category_t

image.png 其中值得注意的是2个方法列表,分别是实例方法列表类方法列表,而不是像类一样类方法在元类。

image.png 方法列表中的方法由_objc_method作为元素存储的 _objc_method包含:方法名,方法签名地址组成。 查看一下

image.png也就是我们源码中的method_t 我们也可以通过查看分类的文档:command + shift + 0搜索category

image.png 当然我们去源码搜索category_t

struct category_t {

    const char *name;

    classref_t cls;

    WrappedPtr<method_list_t, PtrauthStrip> instanceMethods;

    WrappedPtr<method_list_t, PtrauthStrip> classMethods;

    struct protocol_list_t *protocols;

    struct property_list_t *instanceProperties;

    // Fields below this point are not always present on disk.

    struct property_list_t *_classProperties;

    method_list_t *methodsForMeta(bool isMeta) {

        if (isMeta) return classMethods;

        else return instanceMethods;

    }
    property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);

    

    protocol_list_t *protocolsForMeta(bool isMeta) {

        if (isMeta) return nullptr;

        else return protocols;

    }

};

我们可以得出以下结论分类本质一个category_t类型的结构体

  1. 有2个属性name类的名字和cls类对象 。
  2. 2个方法列表,实例方法列表类方法列表
  3. 一个协议列表,一个属性列表 之前我们在探索类的加载的时候进入类methodizeClass方法。

image.png 其中就有判断条件,如果有分类的话进入分类的流程。通过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中分类的数据准备好,处理好最终通过attachLists加入类中

image.png 分类加载主要下面几个步骤:

  1. 加载的时机,是否和类一样?有懒加载和非懒加载
  2. attachCategories准备分类的数据。
  3. attachLists 将分类添加到主类。

4.总结

类的加载分为懒加载非懒加载

  • 在调用load_images中回查询类的+load是否有实现,实现话会实现类的非懒加载方法realizeClassWithoutSwift,其中会对类的方法列表,属性列表等进行赋值。其中方法列表会根据方法地址的高低进行排序,为了后面方法查找的时候通过二分法进行快速查找。
  • 懒加载,为了节约内存,也为了提高程序启动速度。当类第一次发送消息的时候进入慢速查找流程,实现这个类。
  • 关于数据的写入比如方法列表属性列表等,data()在编译的时候已经确定数据类型是class_rw_t,会把它赋值给ro的类型是class_ro_t,rw是包含所有ro的类型的所以可以强转rw->ro. 分类的本质也是category_t的结构体。加载过程也是和类一样分为懒加载和非懒加载,但是分类要添加到主类,这要和主类之间就有了2*2= 4种可能,下一篇章要进行探索分类的加载流程。