OC底层原理探索—类的加载(3)

2021-07-21  本文已影响0人  十年开发初学者

上一篇我们探索了类的加载流程等一系列方法以及懒加载类和非懒加载类这节课我们来探索下分类的加载流程

分类的本质

首先现在main.m中添加LGPerson的分类

@interface LGPerson (LG)

@property (nonatomic, copy) NSString *cateA_name;
@property (nonatomic, assign) int    *cateA_age;

- (void)saySomething;

- (void)cateA_instanceMethod1;
- (void)cateA_instanceMethod2;


@end
@implementation LGPerson (LGA)
- (void)saySomething{
    NSLog(@"%s",__func__);
}
- (void)cateA_instanceMethod1{
    NSLog(@"%s",__func__);
}
- (void)cateA_instanceMethod2{
    NSLog(@"%s",__func__);
}
@end

clang -rewrite-objc main.m -o main.cpp得到main.cpp文件:

struct _category_t {
    const char *name; //名字
    struct _class_t *cls;//主类名
    const struct _method_list_t *instance_methods;//实例方法列表
    const struct _method_list_t *class_methods;//类方法列表
    const struct _protocol_list_t *protocols;//协议
    const struct _prop_list_t *properties;//属性
};

static struct _category_t _OBJC_$_CATEGORY_LGPerson_$_LGA __attribute__ ((used, section ("__DATA,__objc_const"))) = 
{
    "LGPerson",
    0, // &OBJC_CLASS_$_LGPerson,
    (const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_LGPerson_$_LGA,
    0,
    0,
    0,
};

分类的加载

回到上节课所讲的methodizeClass方法

**********************************************************************/
static void methodizeClass(Class cls, Class previously)
{
    runtimeLock.assertLocked();
    bool isMeta = cls->isMetaClass();
    const char *mangledName = cls->nonlazyMangledName();
    if (strcmp(mangledName, "LGPerson") == 0)
    {
        if (!isMeta) {
            printf("%s -LGPerson....\n",__func__);
        }
    }
    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.
    //将属性列表、方法列表、协议列表等贴到rwe中
    // 将ro中的方法列表加入到rwe中
    method_list_t *list = ro->baseMethods();
    if (list) {
        prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls), nullptr);
        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);
    }
}
    class_rw_ext_t *ext() const {
        return get_ro_or_rwe().dyn_cast<class_rw_ext_t *>(&ro_or_rw_ext);
    }

    class_rw_ext_t *extAllocIfNeeded() {
        auto v = get_ro_or_rwe();
        if (fastpath(v.is<class_rw_ext_t *>())) {
            return v.get<class_rw_ext_t *>(&ro_or_rw_ext);
        } else {
            return extAlloc(v.get<const class_ro_t *>(&ro_or_rw_ext));
        }
    }

extAllocIfNeeded是rwe的初始化方法,他的作用是来判断,如果rwe存在,则直接返回,不存在,则需要初始化开辟一个空间

反推

全局搜索extAllocIfNeeded的调用,发现在attachCategories函数中有调用

然后全局搜索attachCategories函数,发现主要集中在attachToClass 和 load_categories_nolock两个方法中调用

  void attachToClass(Class cls, Class previously, int flags)
    {
   ......
            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);
        }
    }

......
  
};
static void load_categories_nolock(header_info *hi) {
......
            if (cls->isStubClass()) {

                if (cat->instanceMethods ||
                    cat->protocols ||
                    cat->instanceProperties ||
                    cat->classMethods ||
                    cat->protocols ||
                    (hasClassProperties && cat->_classProperties))
                {
                    objc::unattachedCategories.addForClass(lc, cls);
                }
            } else {
                if (cat->instanceMethods ||  cat->protocols
                    ||  cat->instanceProperties)
                {
                    if (cls->isRealized()) {
                        attachCategories(cls, &lc, 1, ATTACH_EXISTING);
                    } else {
                        objc::unattachedCategories.addForClass(lc, cls);
                    }
                }

                if (cat->classMethods  ||  cat->protocols
                    ||  (hasClassProperties && cat->_classProperties))
                {
                    if (cls->ISA()->isRealized()) {
                        attachCategories(cls->ISA(), &lc, 1, ATTACH_EXISTING | ATTACH_METACLASS);
                    } else {
                        objc::unattachedCategories.addForClass(lc, cls->ISA());
                    }
                }
            }
        }
    };

}
......

接下来我们反推attachToClass方法,全局搜索该方法,发现该方法仅在methodizeClass中调用

static void methodizeClass(Class cls, Class previously)
{
.......
    //// 加入分类中的方法
    // Attach categories.
    if (previously) {
        if (isMeta) {
            objc::unattachedCategories.attachToClass(cls, previously,
                                                     ATTACH_METACLASS);
        } else {
            objc::unattachedCategories.attachToClass(cls, previously,
                                                     ATTACH_CLASS_AND_METACLASS);
        }
    }
    objc::unattachedCategories.attachToClass(cls, cls,
                                             isMeta ? ATTACH_METACLASS : ATTACH_CLASS);

......

反推load_categories_nolock方法,全局搜索,发现在_read_images中调用

走到这一步我们有两条主线

分类与主类加载的4种情况

首先我们现在_read_images、realizeClassWithoutSwift 、load_categories_nolock、methodizeClass、attachToClass、attachCategories
这6个方法中分别写上判断并打上断点

    const char *mangledName = cls->nonlazyMangledName();
    if (strcmp(mangledName, "LGPerson") == 0)
    {
        if (!isMeta) {
            printf("%s LGPerson....\n",__func__);
        }
    }

接下来我们来实现一个分类

image.png
main中打上断点
image.png

分类+主类同时实现 load方法

主类 分类同时实现load

根据控制台和调用栈的打印 我们得出的流程

分类实现load 主类不实现

image.png

在这种情况下attachCategories没有被调用,只是_read_images相关函数的调用。

流程:_read_imags->realizeClassWithoutSwift ->methodizeClass -> attachToClass

分类不实现load 主类实现load

image.png

这种情况的结果,和上一个一样,attachCategories同样没被调用
流程:_read_imags->realizeClassWithoutSwift ->methodizeClass -> attachToClass

主类 分类 都不实现

image.png
由此可知在程序启动的时候,类并没有加载,当初始化类的时候,通过方法的慢速查找,一步步的走完流程
流程 :lookUpImpOrForward->realizeClassWithoutSwift ->methodizeClass -> attachToClass

流程加载跟踪

load_categories_nolock

从上述流程中得知,只有第一种情况分类主类都实现load的情况下才走了attachCategories,接下来我们来探索load_categories_nolock

image.png
由上图我们看到,count为1,代表分类数目,打印cat,也就是我们分类的信息,我们可以看到分类名是LGA,instanceMethod和classMethods是有值的。 image.png ·
lc是一个结构体类型,里面存储cat和hihi是符号表信息

接下来断点走到attachCategories方法

image.png

attachCategories

断点进入到attachCategories方法

image.png
我们看到cat_count等于1代表1个分类,而mlist中的count等于2代表该分类中有两个方法。
mlist是分类方法的结构体

接下来断点继续往下走


image.png image.png

断点继续往下走,走到prepareMethodLists进行方法排序

image.png
最终走到attachLists方法
image.png

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;
            array_t *newArray = (array_t *)malloc(array_t::byteSize(newCount));
            newArray->count = newCount;
            array()->count = newCount;

            for (int i = oldCount - 1; i >= 0; I--)
                newArray->lists[i + addedCount] = array()->lists[I];
            for (unsigned i = 0; i < addedCount; I++)
                newArray->lists[i] = addedLists[I];
            free(array());
            setArray(newArray);
            validate();
        }
        else if (!list  &&  addedCount == 1) {
            // 0 lists -> 1 list
            list = addedLists[0];
            validate();
        } 
        else {
            // 1 list -> many lists
            Ptr<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;
            for (unsigned i = 0; i < addedCount; I++)
                array()->lists[i] = addedLists[I];
            validate();
        }
    }
image.png

四种情况的分类数据加载

分类 主类均有load

由上面分析可知,分类 主类均有load情况,是在load_imags后全部的动态加载,这里不做更多讲解

分类实现load 主类不实现

realizeClassWithoutSwift打上断点

image.png
分类不实现load 主类实现

这种情况和上述情况一样,分类也是通过data()加载的

主类 分类均不实现

通过方法的慢速查找流程 lookUpImpOrForward->realizeClassWithoutSwift ,最终 分类的数据也是通过data()加载的

多个分类有load 主类有

load_categories_nolock添加断点

image.png
发现count==3由此得出其 流程和分类主类均实现的加载流程是相同的
上一篇 下一篇

猜你喜欢

热点阅读