【iOS】—— 分类,扩展和关联对象

分类,扩展和关联对象

在上周中学习JSONModel源码的过程中,源码里用到了许多关联对象的知识,之前在学小蓝书的时候对这一部分内容学习并不完整,在这作以补充:

分类和扩展

分类概念

  • Category是Objective-C 2.0之后添加的语言特性,分类、类别其实都是指的Category。
  • Category的主要作用是为已经存在的类添加方法。也可以说是将庞大的类代码按逻辑划入几个分区。
  • 分类的特性是可以在运行时阶段动态的为已有的类添加新行为。
  • Objective-C 中Category 就是对装饰模式的一种具体实现。它的主要作用是在不改变原有类的前提下,动态地给这个类添加一些方法。
  • 分类也可以把framework私有方法公开化
    比如我们假如我有一个类里有一个私有方法A 外界是调用不到的 但是我给这个类写了个category 在里里面申明了一个方法(也叫A,只申明,不实现) 现在我import这个category 调用 这个A 的情况是怎么样的呢?实际上这时候就会调用私有方法这个A,我们通过分类将私有方法公开化了。

扩展概念

  • 扩展(Extension)有时候被称为匿名分类。但是两者实质上不是一个内容。
  • 扩展是在编译阶段与该类同时编译的,是类的一部分。扩展中声明的方法只能在该类的@implementation中实现。所以这也就意味着我们无法对系统的类使用扩展。
  • 同时与分类不同,扩展不但可以声明方法,还可以声明成员变量,这是分类所做不到的。

两者区别

  • 1.分类原则上只能增加方法,但是也可以通过关联属性增加属性
  • 2.拓展可以增加方法和属性,都是私有的。
  • 3.扩展只能在自身类中使用,而不是子类或者其他地方。
  • 4.类扩展是在编译阶段添加到类中,而分类是在运行时添加到类中

分类的实质

typedef struct category_t *Category;
struct category_t {
    const char *name;
    classref_t cls;
    struct method_list_t *instanceMethods;  //实例方法
    struct method_list_t *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);
};

  • 从结构体可以知道,有属性列表,所以分类可以声明属性,但是分类只会生成该属性对应的get和set的声明,没有去实现该方法。
  • 结构体没有成员变量列表,所以不能声明成员变量。
  • 方法可以运行时改变,结构体不能运行时改变,要想改变原结构体,增加一个属性,只能用关联变量的方式,而关联本质是在类的定义之外为类增加额外的存储空间,是一层映射关系

关联对象

通过关联对象给分类添加属性

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface NSObject (testCategory)
@property (nonatomic, strong) id associatedObject;
@end

NS_ASSUME_NONNULL_END

首先在分类的.h文件里声明一个属性,然后在main中给属性赋值:

#import <Foundation/Foundation.h>
#import "NSObject+testCategory.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSObject *obj = [[NSObject alloc] init];
        obj.associatedObject = @"1";
        NSLog(@"%@",obj.associatedObject);
        [obj setAssociatedObject:@"2"];
        NSLog(@"%@",obj.associatedObject);
    }
    return 0;
}

我们发现在编译中并没有报错,然而在运行的时候出现崩溃:
在这里插入图片描述

  • 添加的属性系统并没有自动生成成员变量,也没有实现set和get方法,只是生成了set和get方法的声明。这就是为什么在分类中扩展了属性,在外部并没有办法调用。
  • 在外部调用点语法设值和取值,本质其实就是调用属性的set和get方法,现在系统并没有实现这两个方法,所以外部就没法调用分类中扩展的属性。
关联对象的用处
  • 运行时给cagetory添加getter和setter。因为category中添加的property不会生成带下划线"_"的成员变量以及getter和setter的实现。所以可以通过关联对象实现getter和setter。
  • 有时需要在对象中存储一些额外的信息,我们通常会从对象所属的类中继承一个子类。然后给这个子类添加额外的属性,改用这个子类。然而并非所有的情况都能这么做,有时候类的实例可能是由某种机制创建的,而开发者无法另这种机制创建出自己所写的子类实例。此时可以使用“关联对象”。
  • 有时只是给某个类添加一个额外的属性,完全没有必要继承出来一个子类。此时可以使用“关联对象”。
  • delegate回调的方法中使用关联对象。有时候在一些delegate回调的方法中需要处理一些回调任务。比如发起网络请求和在delegate回调的方法中做UI的更新。这样一来,发起网络请求和在回调中更新UI的代码被分散到了两个地方,不利于管理和阅读。此时可以使用“关联对象”。
关联对象的API
添加关联对象
void objc_setAssociatedObject(id object, const void * key, id value, objc AssociationPolicy policy)

获得关联对象
id objc_getAssociatedObject(id object, const void * key)

移除所有的关联对象
void objic_removeAssociatedObjects(id object)

set方法的参数:

  • 参数一:id object : 给哪个对象添加属性。
  • 参数二:const void *key : 关联对象中属性值存取过程中对应唯一标识, 根据key来设置和取值。
  • 参数三:id value : 关联的值,也就是set方法传入的值给属性去保存。
  • 参数四:objc AssociationPolicy policy : 策略,属性以什么形式保存。

参数二:

	给key设值有三种比较好的的方法
	因为key值类型为const void*,任何类型的值,其实就是给个唯一的标识
	
	1.我们针对每个属性,定义-个全局的key名, 然后取其地址,这一定是唯一的加上static,只在文件内部有效
	static const void *NameKey = &NameKey;
	static const void *WeightKey = &WeightKey;
	
	2.针对每个属性,因为类中的属性名是唯一的,直接拿属性名作为key
	#define NameKey = @"name";
	#define WeightKey = @"weight";
	
	3.使用@selector作为key
	直接用属性名对应的get方法的selector,有提示不容易写错
	并且get方法隐藏参数cmd 可以直接用,看上去就会更加简洁
	以下实例代码就是用的第三种方式

参数四枚举类型:

typedef OBJC ENUM(uintptr_t, objc_AssociationPolicy) {
	OBJC_ ASSOCIATION_ASSIGN = 0, //指定个弱引用相关联的对象
	OBJC_ ASSOCIATION_RETAIN_NONATOMIC = 1; //指定相关对象的强引用, 非原子性
	OBJC_ ASSOCIATION_COPY_NONATOMIC = 3; //指定相关的对象被复制, 非原子性
	OBJC_ ASSOCIATION_RETAIN = 01401; //指定相关对象的强引用,原子性
	OBJC_ ASSOCIATION_COPY = 01403; //指定相关的对象被复制, 原子性
};
给声明的属性添加setter,getter方法
#import "NSObject+testCategory.h"
#import <objc/runtime.h> // 关联对象相关api在runtime库中
@implementation NSObject (testCategory)
- (void)setAssociatedObject:(id)object {

    objc_setAssociatedObject(self, @selector(associatedObject), object, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}

- (id)associatedObject {

    return objc_getAssociatedObject(self, _cmd);

}

@end

这时候运行main代码:
在这里插入图片描述

在get方法中我们看到了_cmd参数,这是个什么东西呢?

  • _cmd 是隐藏的参数,表示当前方法的selector,他和self表示当前方法调用的对象实例。
  • 获取当前被调用方法: NSStringFromSelector(_cmd)
  • 在运行时时使用:在某个分类方法里为对象动态添加属性,由于_cmd是在编译时候(compile - time)就可以确定的值,因此可以直接使用

关联对象底层源码

实现关联对象的核心对象有

  • AssociationsManager
  • AssociationsHashMap
  • ObjectAssociationMap
  • ObjcAssociation

objc_setAssociatedObject(self, @selector(associatedObject), object, OBJC_ASSOCIATION_RETAIN_NONATOMIC);实际调用下面的方法:

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.
    //isa有一位信息为禁止关联对象,如果设置了,直接报错
    if (!object && !value) return;
    // 判断runtime版本是否支持关联对象
    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));
    // 将 object 封装成 DisguisedPtr 目的是方便底层统一处理
    DisguisedPtr<objc_object> disguised{(objc_object *)object};
    // // 将 policy和value 封装成ObjcAssociation,目的是方便底层统一处理
    ObjcAssociation association{policy, value};
    // (如果有新值)保留锁外的新值。
    // retain the new value (if any) outside the lock.
    // 根据传入的缓存策略,创建一个新的value对象
    association.acquireValue();

    bool isFirstAssociation = false;
    {
    //调用构造函数,构造函数内加锁操作
        AssociationsManager manager; 
        // 创建一个管理对象管理单例,类AssociationsManager管理一个锁/哈希表单例对。分配一个实例将获得锁
        // 并不是全场唯一,构造函数中加锁只是为了避免重复创建,在这里是可以初始化多个AssociationsManager变量的
        
        //获取全局的HasMap
        // 全场唯一
        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 */
                // 这是我们建立的第一个关联
                //说明是第一次设置关联对象,把是否关联对象设置为YES
                isFirstAssociation = true;
            }
            // 建立或替换关联
            /* establish or replace the association */
            // 获取ObjectAssociationMap中存储值的地址
            auto &refs = refs_result.first->second;
            // 移除之前的关联,根据key
            // 将需要存储的值存放在关联表中存储值的地址中
            // 同时会根据key去查找,如果查找到`result.second` = false ,如果找不到就创建`result.second` = true
            // 创建association时,当(association的个数+1)超过3/4,就会进行两倍扩容
            auto result = refs.try_emplace(key, std::move(association));
            if (!result.second) {
                // 交换association和查询到的`association`
                // 其实可以理解为更新查询到的`association`数据,新值替换旧值
                association.swap(result.first->second);
            }
        } else {
            // 这里相当于传入的nil,移除之前的关联
            // 到AssociationsHashMap找到ObjectAssociationMap,将传入key对应的值变为空。
            // 查找disguised 对应的ObjectAssociationMap
            auto refs_it = associations.find(disguised);
            // 如果找到对应的 ObjectAssociationMap 对象关联表
            if (refs_it != associations.end()) {
                // 获取 refs_it->second 里面存放了association类型数据
                auto &refs = refs_it->second;
                // 根据key查询对应的association
                auto it = refs.find(key);
                if (it != refs.end()) {
                    // 如果找到,更新旧的association里面的值
                    association.swap(it->second);
                    refs.erase(it);
                    if (refs.size() == 0) {
                        // 如果该对象关联表中所有的关联属性数据被清空,那么该对象关联表会被释放
                        associations.erase(refs_it);

                    }
                }
            }
        }
    }
    // 在锁外面调用setHasAssociatedObjects,因为如果对象有一个,这个//将调用对象的noteAssociatedObjects方法,这可能会触发initialize,这可能会做任意的事情,包括设置更多的关联对象。
    if (isFirstAssociation)
        object->setHasAssociatedObjects();

    // release the old value (outside of the lock).
    // 释放旧的值(在锁外部)
    association.releaseHeldValue();
}

objc_getAssociatedObject(self, _cmd)实际是调用下面的方法:

id
_object_get_associative_reference(id object, const void *key)
{
    ObjcAssociation association{};//创建空的关联对象

    {
        AssociationsManager manager;//创建一个AssociationsManager管理类
        AssociationsHashMap &associations(manager.get());//获取全局唯一的静态哈希map
        AssociationsHashMap::iterator i = associations.find((objc_object *)object);//找到迭代器,即获取buckets
        if (i != associations.end()) {//如果这个迭代查询器不是最后一个 获取
            ObjectAssociationMap &refs = i->second; //找到ObjectAssociationMap的迭代查询器获取一个经过属性修饰符修饰的value
            ObjectAssociationMap::iterator j = refs.find(key);//根据key查找ObjectAssociationMap,即获取bucket
            if (j != refs.end()) {
                association = j->second;//获取ObjcAssociation
                association.retainReturnedValue();
            }
        }
    }

    return association.autoreleaseReturnedValue();//返回value
}



最后来看void objic_removeAssociatedObjects(id object):

// Unlike setting/getting an associated reference,
// this function is performance sensitive because of
// raw isa objects (such as OS Objects) that can't track
// whether they have associated objects.
// 与设置/获取关联引用不同,此函数对性能敏感,因为原始isa对象(如OS对象)不能跟踪它们是否有关联对象。
void
_object_remove_assocations(id object, bool deallocating)
{
    ObjectAssociationMap refs{};

    {
        AssociationsManager manager;
        AssociationsHashMap &associations(manager.get());
        AssociationsHashMap::iterator i = associations.find((objc_object *)object);
        if (i != associations.end()) {
            refs.swap(i->second);

            // If we are not deallocating, then SYSTEM_OBJECT associations are preserved.
            //如果我们没有回收,那么SYSTEM_OBJECT关联会被保留。
            bool didReInsert = false;
            if (!deallocating) {
                for (auto &ref: refs) {
                    if (ref.second.policy() & OBJC_ASSOCIATION_SYSTEM_OBJECT) {
                        i->second.insert(ref);
                        didReInsert = true;
                    }
                }
            }
            if (!didReInsert)
                associations.erase(i);
        }
    }

    // Associations to be released after the normal ones.
    // 在正常关联之后释放关联。
    SmallVector<ObjcAssociation *, 4> laterRefs;

    // release everything (outside of the lock).
    // 释放锁外的所有内容。
    for (auto &i: refs) {
        if (i.second.policy() & OBJC_ASSOCIATION_SYSTEM_OBJECT) {
            // If we are not deallocating, then RELEASE_LATER associations don't get released.
            //如果我们不是在释放,那么RELEASE_LATER关联不会被释放
            if (deallocating)
                laterRefs.append(&i.second);
        } else {
            i.second.releaseHeldValue();
        }
    }
    for (auto *later: laterRefs) {
        later->releaseHeldValue();
    }
}

总结
设值流程
  • 创建一个AssociationsManager 管理类
  • 获取唯一全局静态哈希Map
  • 判断是否存在关联对象值
  • 存在 :
    创建一个空的 ObjectAssociationMap 去取查询的键值对
    如果发现没有这个 key 就先插入一个 空的 BucketT
    标记对象存在关联对象
    用当前 策略 policy 和 值 value 组成了一个 ObjcAssociation 替换之前空的BucketT
    标记 ObjectAssociationMap 为 第二次
  • 不存在 :
    根据DisguisedPtr 找到 AssociationsHashMap 中的 iterator 迭代查询器
    清理迭代器
取值流程
  • 创建一个 AssociationsManager 管理类
  • 获取唯一的全局静态哈希Map
  • 根据 DisguisedPtr 找到 AssociationsHashMap 中的 iterator 迭代查询器
  • 如果这个 迭代查询器 != associations.end(), 即不是最后一个, 那么获取 : ObjectAssociationMap (这里有策略 policy 和 值 value)
  • ObjectAssociationMap的迭代查询器获取一个经过属性修饰符修饰的value
  • 返回value
销毁流程
  • 对象销毁dealloc时,销毁相关的关联对象。
  • 调用流程:dealloc --> _objc_rootDealloc --> rootDealloc --> object_dispose --> objc_destructInstance --> _object_remove_assocations。

除此之外还看了一些分类的运行期之类的东西,但是没太看懂,后面再补充。

4月4日补充:

分类在运行期做了什么

_objc_init这个函数是runtime的初始化函数,我们从_objc_init开始入手

_objc_init

/***********************************************************************
* _objc_init
* Bootstrap initialization. Registers our image notifier with dyld.
* Called by libSystem BEFORE library initialization time
**********************************************************************/

void _objc_init(void)
{
    static bool initialized = false;
    if (initialized) return;
    initialized = true;
    
    // fixme defer initialization until an objc-using image is found?
    environ_init();
    static_init();
    runtime_init();
    exception_init();
    cache_t::init();

    _imp_implementationWithBlock_init();

    _dyld_objc_callbacks_v1 callbacks = {
        1, // version
        &map_images,
        load_images,
        unmap_image,
        _objc_patch_root_of_class
    };
    _dyld_objc_register_callbacks((_dyld_objc_callbacks*)&callbacks);

    didCallDyldNotifyRegister = true;
}

map_images读取资源(images代表资源模块),我们点进去看看:

void
map_images(unsigned count, const char * const paths[],
           const struct mach_header * const mhdrs[])
{
    bool takeEnforcementDisableFault;

    {
        mutex_locker_t lock(runtimeLock);
        map_images_nolock(count, paths, mhdrs, &takeEnforcementDisableFault);
    }

    if (takeEnforcementDisableFault) {
#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
        bool objcModeNoFaults = DisableFaults
            || DisableClassROFaults
            || getpid() == 1
            || is_root_ramdisk()
            || !os_variant_has_internal_diagnostics("com.apple.obj-c");
        if (!objcModeNoFaults) {
            os_fault_with_payload(OS_REASON_LIBSYSTEM,
                                  OS_REASON_LIBSYSTEM_CODE_FAULT,
                                  NULL, 0,
                                  "class_ro_t enforcement disabled",
                                  0);
        }
#endif
    }
}

然后再点进去map_images_nolock方法:
这个方法实现太长了,就不放出来了,咱们重点看的是这个方法里面的_read_images方法:
这个方法也很长,就放出分类相关这一部分代码:

_read_images

// Discover categories. 
    for (EACH_HEADER) {
        category_t **catlist = 
            _getObjc2CategoryList(hi, &count);
        bool hasClassProperties = hi->info()->hasCategoryClassProperties();

        for (i = 0; i < count; i++) {
            category_t *cat = catlist[i];
            Class cls = remapClass(cat->cls);

            if (!cls) {
                catlist[i] = nil;
                if (PrintConnecting) {
                    _objc_inform("CLASS: IGNORING category \?\?\?(%s) %p with "
                                 "missing weak-linked target class", 
                                 cat->name, cat);
                }
                continue;
            }
            bool classExists = NO;
            if (cat->instanceMethods ||  cat->protocols  
                ||  cat->instanceProperties) 
            {
                addUnattachedCategoryForClass(cat, cls, hi);
                if (cls->isRealized()) {
                    remethodizeClass(cls);
                    classExists = YES;
                }
                if (PrintConnecting) {
                    _objc_inform("CLASS: found category -%s(%s) %s", 
                                 cls->nameForLogging(), cat->name, 
                                 classExists ? "on existing class" : "");
                }
            }

            if (cat->classMethods  ||  cat->protocols  
                ||  (hasClassProperties && cat->_classProperties)) 
            {
                addUnattachedCategoryForClass(cat, cls->ISA(), hi);
                if (cls->ISA()->isRealized()) {
                    remethodizeClass(cls->ISA());
                }
                if (PrintConnecting) {
                    _objc_inform("CLASS: found category +%s(%s)", 
                                 cls->nameForLogging(), cat->name);
                }
            }
        }
    }

我们来看看这个方法都干了什么:

  • 获取category列表list
  • 遍历category list中的每一个category
  • 获取category的对应的主类cls,如果没有cls就跳过(continue)这个继续获取下一个
  • 如果其有对应的主类,并其有实例方法、协议、属性,则调用addUnattachedCategoryForClass,同时如果cls中有实现的话,进一步调用remethodizeClass方法
  • 如果其有对应的主类,并其有类方法、协议,则调用addUnattachedCategoryForClass,同时如果cls的元类有实现的话,就进一步调用remethodizeClass方法

4、5主要是分类对应的主类是元类对象还是类对象

然后我们来看看上面方法中用到的addUnattachedCategoryForClass方法:

addUnattachedCategoryForClass

static void addUnattachedCategoryForClass(category_t *cat, Class cls, 
                                          header_info *catHeader)
{
    runtimeLock.assertWriting();

    // DO NOT use cat->cls! cls may be cat->cls->isa instead
    NXMapTable *cats = unattachedCategories();
    category_list *list;

    list = (category_list *)NXMapGet(cats, cls);
    if (!list) {
        list = (category_list *)
            calloc(sizeof(*list) + sizeof(list->list[0]), 1);
    } else {
        list = (category_list *)
            realloc(list, sizeof(*list) + sizeof(list->list[0]) * (list->count + 1));
    }
    list->list[list->count++] = (locstamped_category_t){cat, catHeader};
    NXMapInsert(cats, cls, list);
}
static NXMapTable *unattachedCategories(void)
{
    runtimeLock.assertWriting();
    //全局对象
    static NXMapTable *category_map = nil;
    if (category_map) return category_map;
    // fixme initial map size
    category_map = NXCreateMapTable(NXPtrValueMapPrototype, 16);
    return category_map;
}

我们来看看这个方法都干了什么:

  • 通过unattachedCategories()函数生成一个全局对象cats
  • 我们从这个单例对象中查找cls,获取一个category_list *list列表
  • 要是没有list指针。那么我们就生成一个category_list空间
  • 要是有list指针,我们就在该指针的基础上再分配处category_list大小的空间
  • 在这新分配好的空间,将这个cat和catHeader写入
  • 将数据插入到cats中,key----->cls value------>list

这段代码对于我们来说,对分类的实现部分关系并不大,其仅仅是把类和category做一个关联映射,而remethodizeClass才是真正去处理添加事宜的功臣

remethodizeClass

static void remethodizeClass(Class cls)
{
    //分类数组
    category_list *cats;
    bool isMeta;

    runtimeLock.assertWriting();

    isMeta = cls->isMetaClass();

    // Re-methodizing: check for more categories
    if ((cats = unattachedCategoriesForClass(cls, false/*not realizing*/))) {
        if (PrintConnecting) {
            _objc_inform("CLASS: attaching categories to class '%s' %s", 
                         cls->nameForLogging(), isMeta ? "(meta)" : "");
        }
        
        attachCategories(cls, cats, true /*flush caches*/);        
        free(cats);
    }
}

还是没有得到我们需要的信息,其核心是调用了attachCategories函数把我们的分类信息附加到该类中

attachCategories

static void 
attachCategories(Class cls, category_list *cats, bool flush_caches)
{
    if (!cats) return;
    if (PrintReplacedMethods) printReplacements(cls, cats);

    bool isMeta = cls->isMetaClass();

    // 创建方法列表、属性列表、协议列表,用来存储分类的方法、属性、协议
    method_list_t **mlists = (method_list_t **)
        malloc(cats->count * sizeof(*mlists));
    property_list_t **proplists = (property_list_t **)
        malloc(cats->count * sizeof(*proplists));
    protocol_list_t **protolists = (protocol_list_t **)
        malloc(cats->count * sizeof(*protolists));

    // Count backwards through cats to get newest categories first
    int mcount = 0;           // 记录方法的数量
    int propcount = 0;        // 记录属性的数量
    int protocount = 0;       // 记录协议的数量
    int i = cats->count;      // 从分类数组最后开始遍历,保证先取的是最新的分类
    bool fromBundle = NO;     // 记录是否是从 bundle 中取的
    while (i--) { // 从后往前依次遍历
        auto& entry = cats->list[i];  // 取出当前分类
    
        // 取出分类中的方法列表。如果是元类,取得的是类方法列表;否则取得的是对象方法列表
        method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
        if (mlist) {
            mlists[mcount++] = mlist;            // 将方法列表放入 mlists 方法列表数组中
            fromBundle |= entry.hi->isBundle();  // 分类的头部信息中存储了是否是 bundle,将其记住
        }

        // 取出分类中的属性列表,如果是元类,取得的是 nil
        property_list_t *proplist = 
            entry.cat->propertiesForMeta(isMeta, entry.hi);
        if (proplist) {
            proplists[propcount++] = proplist;
        }

        // 取出分类中遵循的协议列表
        protocol_list_t *protolist = entry.cat->protocols;
        if (protolist) {
            protolists[protocount++] = protolist;
        }
    }
    // 取出当前类 cls 的 class_rw_t 数据
    auto rw = cls->data();

    // 存储方法、属性、协议数组到 rw 中【注意是rw哦】
    // 准备方法列表 mlists 中的方法【为什么需要准备方法列表这一步?】
   
    prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
    // 将新方法列表添加到 rw 中的方法列表中
    rw->methods.attachLists(mlists, mcount);
    // 释放方法列表 mlists
    free(mlists);
    // 清除 cls 的缓存列表
    if (flush_caches  &&  mcount > 0) flushCaches(cls);

    // 将新属性列表添加到 rw 中的属性列表中
    rw->properties.attachLists(proplists, propcount);
    // 释放属性列表
    free(proplists);

    // 将新协议列表添加到 rw 中的协议列表中
    rw->protocols.attachLists(protolists, protocount);
    // 释放协议列表
    free(protolists);
}

先创建方法列表、属性列表、协议列表的新列表并且给它们分配内存,然后存储该cls所有的分类的方法、属性、协议,然后转交给了attachMethodLists方法

attachLists方法保证其添加到列表的前面

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;
            setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
            array()->count = newCount;
            memmove(array()->lists + addedCount, array()->lists, 
                    oldCount * sizeof(array()->lists[0]));
            memcpy(array()->lists, addedLists, 
                   addedCount * sizeof(array()->lists[0]));
        }
        else if (!list  &&  addedCount == 1) {
            // 0 lists -> 1 list
            list = addedLists[0];
        } 
        else {
            // 1 list -> many lists
            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;
            memcpy(array()->lists, addedLists, 
                   addedCount * sizeof(array()->lists[0]));
        }
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值