分类的加载流程

非懒加载类的加载流程

首先非懒加载的类 会走到一个 realizeClassWithoutSwift 的方法里面,请添加图片描述
给rw创建空间分配好内存数据以后,就会设置父类元类请添加图片描述
最后就是处理分类请添加图片描述
下面就是methodizeclass方法的源码

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

分类的加载

通过创建一个分类然后在分类上声明上属性和方法然后进行探究,请添加图片描述
然后cd到文件目录下clang -rewrite-objc main.m 生成main.cpp文件然后前往文件末尾。请添加图片描述
在这里插入图片描述
然后可以看到class_methods ,为什么在分类可以看到类方法。之前的篇章我们知道类方法储存在元类,所以编译的时候类方法储存在分类。也可说分类没有元类。

分类的属性绑定

static  const char *LGNameKey = "***";
//利用runtime进行数据绑定方式让编译可以进行,让主类能够访问
-(voidstName:(NSString*)name[
objc_setAssociatedObject(self,LGNameKey,name,OBJC_ASSOCIATION_COPT_NONATOMIC);
]

-(NSString*)name{
	objc_getAssociatedObject(self,LGNameKey);
}

接下来看看那objc_setAssociatedObject 函数底层做了什么:

void
objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
{
    _object_set_associative_reference(object, key, value, policy);
}
void
_object_set_associative_reference(id object, const void *key, id value, uintptr_t policy)
{
    // This code used to work when nil was passed for object and key. Some code
    // probably relies on that to not crash. Check and handle it explicitly.
    // rdar://problem/44094390
    if (!object && !value) return;

    if (object->getIsa()->forbidsAssociatedObjects())
        _objc_fatal("objc_setAssociatedObject called on instance (%p) of class %s which does not allow associated objects", object, object_getClassName(object));

    DisguisedPtr<objc_object> disguised{(objc_object *)object};
    ObjcAssociation association{policy, value};

    // retain the new value (if any) outside the lock.
    association.acquireValue();

    bool isFirstAssociation = false;
    {
        AssociationsManager manager;
        AssociationsHashMap &associations(manager.get());

        if (value) {
            auto refs_result = associations.try_emplace(disguised, ObjectAssociationMap{});
            if (refs_result.second) {
                /* it's the first association we make */
                isFirstAssociation = true;
            }

            /* establish or replace the association */
            auto &refs = refs_result.first->second;
            auto result = refs.try_emplace(key, std::move(association));
            if (!result.second) {
                association.swap(result.first->second);
            }
        } else {
            auto refs_it = associations.find(disguised);
            if (refs_it != associations.end()) {
                auto &refs = refs_it->second;
                auto it = refs.find(key);
                if (it != refs.end()) {
                    association.swap(it->second);
                    refs.erase(it);
                    if (refs.size() == 0) {
                        associations.erase(refs_it);
                    }
                }
            }
        }
    }

    // Call setHasAssociatedObjects outside the lock, since this
    // will call the object's _noteAssociatedObjects method if it
    // has one, and this may trigger +initialize which might do
    // arbitrary stuff, including setting more associated objects.
    if (isFirstAssociation)
        object->setHasAssociatedObjects();

    // release the old value (outside of the lock).
    association.releaseHeldValue();
}

接下来是讲解部分,代码通过把传进来的object封装成DisguisePtr<objc_object>,然后对policy , value 封装成ObjcAssociation。然后是association.acquireValue对policy进行处理。

AssociationsManager manager;
==》
class AssociationsManager {
    using Storage = ExplicitInitDenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap>;
    static Storage _mapStorage;

public:
    AssociationsManager()   { AssociationsManagerLock.lock(); }
    ~AssociationsManager()  { AssociationsManagerLock.unlock(); }

    AssociationsHashMap &get() {
        return _mapStorage.get();
    }

    static void init() {
        _mapStorage.init();
    }
};

可以看到AssociationsManager manager;意思是在_object_set_associative_reference的函数结束之前都会使用线程锁保证同一时间下只有一个线程在操作AssociationsHashMap(散列表)。
如果value有值,那么就通过迭代器来刷新的HASHMAP里面的值。如果value没值就把关联删除掉。

分类的方法如何被主类调用

首先分类和类一样要先被加载,也和类一样分为懒加载和非懒加载。当我们通过runtime动态加载类的方法和属性,或者当存在分类属于非懒加载类的时候编译以后就会存在class_rw_ext_t 。所以通过判断class_rw_ext_t的存在与否就可以得出分类是在什么时候被加载的了。
首先通过cd到目录下然后clang -rewrite-objc main.m, 生成.cpp文件然后进入。自定义一个类和分类然后alloc 。通过搜索objc_class:

  1. 先找到class_data_bits_t
  2. 找到 class_rw_t * data()
  3. 找到class_rw_ext_t *extAllocIfNeeded()
  4. 搜索extAllocIfNeeded
  5. 找到attachCategories *
  6. 搜索attachCategories
  7. 找到attachToclass *
  8. 找到load_categories_nolock *

然后通过在attachCategories方法下auto rwe = cls->data()->extAllocIfNeed(); 后加上代码片段并打上断点如图:
请添加图片描述
在attachToclass方法下也加上代码片段,如图:请添加图片描述
在方法load_categories_nolock下加上片段:请添加图片描述
然后通过main.mm下加上代码

//
//  main.m
//  SXObjcDebug
//
//  Created by Allin on 2022/4/13.
//

#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <malloc/malloc.h>

@interface LGPerson : NSObject

@property (nonatomic ,copy) NSString *hobby;
-(void)printClassAllMethod:(Class)cls;
-(void)test;

@end

@implementation LGPerson

+(void)load {
    
}

-(void)test {
    NSLog(@"%s",__func__);
}

#pragma mark - 打印当前类的MethodList
-(void)printClassAllMethod:(Class)cls{
    
    unsigned int count = 0;
    Method *methodList = class_copyMethodList(cls, &count);
    for (unsigned int i = 0; i<count; i++) {
        Method method = methodList[i];
        SEL sel = method_getName(method);
        IMP imp = class_getMethodImplementation(cls, sel);
        NSLog(@"%@-%p",NSStringFromSelector(sel),imp);
    }
    free(methodList);
}

@end

@interface LGPerson (LG)

@property (nonatomic ,copy) NSString *name;
@property (nonatomic ,assign) int age;
-(void)test;
-(void)category_instanceMethod;
+(void)categoty_classMethod;


@end

@implementation LGPerson (LG)

+(void)load {
    
}

-(void)test {
    NSLog(@"%s",__func__);
}

-(void)category_instanceMethod {
    NSLog(@"%s",__func__);
}

+(void)categoty_classMethod {
    NSLog(@"%s",__func__);
}

static const char *LGNameKey = "LGNameKey";
-(void)setName:(NSString *)name {
    objc_setAssociatedObject(self, LGNameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

-(NSString *)name {
    return objc_getAssociatedObject(self, LGNameKey);
}


@end

struct LGObjc {
    LGObjc() { printf("%s \n",__func__); }
    ~LGObjc() { printf("%s \n",__func__); }
};

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSLog(@"Hello, World!");
        
        LGPerson *p = [LGPerson alloc];
        [p printClassAllMethod:p.class];
        
        LGObjc objc;
    }
    return 0;
}

通过运行断点,最终得知在加载到attachCategories方法以后,通过for循环给rwe赋值 请添加图片描述

然后继续往下查看attachList

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

从这里可以看到,分类在这里相当于一个list , 通过区分不同情况,原来的类没有方法就直接把分类的方法加载进去;
原来的类有方法,通过循环把原来的方法放倒后面新的方法加入到前面。
如果有很多的分类也是这样先把,原来主类方法放到数组后面然后再收集到的分类方法放到前面去。

类(非)分类(非) – ro里面只有类数据没有分类 – 有rwe(分类数据)
类(是)分类(非) – 类和分类里面的数据在ro – 没有rwe
类(非)分类(是) – 类和分类里面的数据在ro – 没有rwe
类(是)分类(是) – 类和分类里面的数据在ro – 没有rwe

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值