iOS Category使用及其源码分析

Category基本使用

分类和类一样都是在接口内声明,在类文件内实现。但是分类不可声明实例变量,只可声明属性和方法,并且分类的实现部分不能包含@synthesize。分类的接口中含有属性声明时,实现部分就要手动定义属性的访问方法。这样是为了防止随意访问同一个类的不同文件中定义的实例变量。

方法可以是实例方法也可为类方法。

语法:Category声明

注意:类名必须是已存在的类,不可定义未存在的类。

@interface 类名(分类名)

属性的声明

方法的声明

@end

语法:Category实现

@implementation 类名 (分类名)

属性的setter定义

属性的getter定义

方法的定义

@end

分类接口部分遵循原则:

1、分类的接口部分必须引用所属类的接口文件;

2、分类实现部分必须引用对应的接口文件;

3、使用分类中的方法时候,必须引用此方法所在的头文件;

例:MyModel.h

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface MyModel : NSObject
@property (nonatomic,copy)NSString *name;
@property (nonatomic,assign)NSInteger age;
@property (nonatomic,assign)CGFloat height;
- (void)work;
@end

NS_ASSUME_NONNULL_END  

MyModel.m

#import "MyModel.h"

@implementation MyModel

- (void)work{
    NSLog(@"-------MyModel work---------");
}

@end

MyModel+Test.h

#import "MyModel.h"

NS_ASSUME_NONNULL_BEGIN

@interface MyModel (Test)
- (void)walk;
@end

NS_ASSUME_NONNULL_END

MyModel+Test.m


#import "MyModel+Test.h"
@implementation MyModel (Test)
  
- (void)walk{
    NSLog(@"----Test walk--------");
}
@end

给已存在类追加分类

追加新方法

无论是自定义还是系统的类,皆可为已存在类追加新方法。

虽然分类不可用追加实例变量,但是新追加的方法可以访问类中属性和方法。借此,我们可以为已有类增加新功能。

当我们在使用继承来为已有类增加新功能感觉麻烦时候,可以通过分类来为正在使用的类增加新功能。

例:通过为系统的NSDate类来增加新方法,获取当前时间并返回一个指定格式的时间字符串。

NSDate+Format.h

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface NSDate (Format)
+(NSString *)getCurrentDayTimeDetail;
@end

NS_ASSUME_NONNULL_END

NSDate+Format.m


#import "NSDate+Format.h"

@implementation NSDate (Format)

+(NSString *)getCurrentDayTimeDetail {
    NSDate *now = [self date];
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    [formatter setDateFormat:@"yyyy年MM月dd日 HH:mm:ss"];
    [formatter setLocale:[[NSLocale alloc]initWithLocaleIdentifier:@"zh_CN"]];
    NSTimeZone *timeZone = [[NSTimeZone alloc]initWithName:@"Asia/Shanghai"];
    [formatter setTimeZone:timeZone];
    NSString *current = [formatter stringFromDate:now];
    return current;
}

@end

覆盖现有方法

新定义分类中的方法如果和原有方法重名,新定义的方法会覆盖老的方法。通过此种方式,可以不使用继承来实现方法覆盖。但是如果不留意覆盖了原有方法,也是会引起不可预测的问题,尤其是覆盖了比较重要的方法,极有可能发生严重的问题。如果多个重复名称方法,就无法知道到底执行了哪个方法。覆盖并不提示警告,因此在使用过程中,一定要注意避免方法被覆盖掉。

关联引用

通过分类,我们可以为一个类追加新的方法,但是不能追加实例变量。但是,借助Objective-C 的运行时功能,可以为已经存在的实例对象增加实例变量。通过这种方式和分类合起来使用,不创建子类,也可以对类进行动态的扩展。关联引用在运行时中不仅可根据需要为对象添加关联,也可以对已添加关联进行移除。

添加关联

 void objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key, id _Nullable value, objc_AssociationPolicy policy);

object:所属类,即要增加关联的对象;

key:关键字,用来区分关联引用,必须使用确定的、不再改变的地址作为键值;

value :引用对象;

policy:用来指定关联引用的存储策略。

此方法通过设置value 为nil,可以删除key 的关联。

检索关联

 id _Nullable objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key);

通过关键字key来检索关联对象object,如果没有关联到任何对象,则返回值为nil。

移除关联

Objective-C 运行时移除关联的方法如下,这个方法会移除object对象的所有关联,谨慎使用。

已存在代码可能已经使用了关联,因此不建议使用此方法,可使用objc_setAssociatedObject,设置参数为nil,以此来分别移除关联。

void objc_removeAssociatedObjects(id _Nonnull object);

关联策略

OBJC_ASSOCIATION_ASSIGN

内存管理时,不给关联对象发送retain消息,仅仅通过赋值进行关联。弱引用。

OBJC_ASSOCIATION_RETAIN_NONATOMIC

内存管理时,会给关联对象发送retain消息并持有,如果同样的key已经关联了其他对象,则会给其他对象发送release消息。释放关联对象的所有者时,会给所有的关联对象发送release消息。强引用。

OBJC_ASSOCIATION_COPY_NONATOMIC:

在进行对象关联引用时候会复制一份原对象,并用新复制的对象进行关联操作。

OBJC_ASSOCIATION_RETAIN:

在对象持有方面和 OBJC_ASSOCIATION_RETAIN_NONATOMIC一样,唯一区别是OBJC_ASSOCIATION_RETAIN是多线程安全的,支持排他性的关联操作。objc_getAssociatedObject的操作和OBJC_ASSOCIATION_RETAIN一样。

OBJC_ASSOCIATION_COPY:

在对象持有方面和 OBJC_ASSOCIATION_COPY_NONATOMIC一样,唯一区别是OBJC_ASSOCIATION_COPY是多线程安全的,支持排他性的关联操作。

例:MyModel+Test.h

#import "MyModel.h"

NS_ASSUME_NONNULL_BEGIN
@protocol MyModelProtocol <NSObject>

- (void)myModelProtocolAction;

@end
  
@interface MyModel (Test)<MyModelProtocol>
  //不会自动生成实例变量,在这里添加属性,其实是添加的setter和getter方法。
@property (nonatomic,copy)NSString *email;
- (void)walk;
@end

NS_ASSUME_NONNULL_END

MyModel+Test.m

#import "MyModel+Test.h"
#import <objc/runtime.h>
@implementation MyModel (Test)

- (void)setEmail:(NSString *)email{
    objc_setAssociatedObject(self, @"email",email, OBJC_ASSOCIATION_COPY);//添加关联
//    objc_setAssociatedObject(self, @"email",nil, OBJC_ASSOCIATION_COPY);//移除关联
}

- (NSString *)email{
    //检索关联
    return  objc_getAssociatedObject(self, @"email");
}

+(void)load{
    NSLog(@"----Test load-------");
}

//+ (void)initialize{
//    NSLog(@"----Test initialize-------");
//}
//

- (void)walk{
    NSLog(@"----Test walk-------%@",self.email);
}

- (void)myModelProtocolAction{
    NSLog(@"----myModelProtocolAction-------");
}


@end

底层结构

通过终端命令:clang -rewrite-objc MyModel+Test.m 生成.cpp格式的文件。

打开重写后生成的.cpp 文件,查找_category_t,在文件末尾找到如下结构体。

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 /*_method_list_t*/ {
    unsigned int entsize;  // sizeof(struct _objc_method)
    unsigned int method_count;
    struct _objc_method method_list[4];
} _OBJC_$_CATEGORY_INSTANCE_METHODS_MyModel_$_Test __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    4,
    {{(struct objc_selector *)"setEmail:", "v24@0:8@16", (void *)_I_MyModel_Test_setEmail_},
    {(struct objc_selector *)"email", "@16@0:8", (void *)_I_MyModel_Test_email},
    {(struct objc_selector *)"walk", "v16@0:8", (void *)_I_MyModel_Test_walk},
    {(struct objc_selector *)"myModelProtocolAction", "v16@0:8", (void *)_I_MyModel_Test_myModelProtocolAction}}
};

类方法结构体,存储着类的方法

static struct /*_method_list_t*/ {
    unsigned int entsize;  // sizeof(struct _objc_method)
    unsigned int method_count;
    struct _objc_method method_list[1];
} _OBJC_$_CATEGORY_CLASS_METHODS_MyModel_$_Test __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    1,
    {{(struct objc_selector *)"load", "v16@0:8", (void *)_C_MyModel_Test_load}}
};

协议列表结构体和属性列表结构体,存储着协议和属性,值得注意的是,在编译后的文件中,并没有找到像类文件编译后那样的_ivar_list_t成员变量结构体和settergetter方法,这也证明了分类不可以添加成员变量。而属性的settergetter 方法,编译后的是走的我们手动添加的相关的关联引用的方法。

//协议
static struct /*_protocol_list_t*/ {
    long protocol_count;  // Note, this is 32/64 bit
    struct _protocol_t *super_protocols[1];
} _OBJC_CATEGORY_PROTOCOLS_$_MyModel_$_Test __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    1,
    &_OBJC_PROTOCOL_MyModelProtocol
};

//属性
static struct /*_prop_list_t*/ {
    unsigned int entsize;  // sizeof(struct _prop_t)
    unsigned int count_of_properties;
    struct _prop_t prop_list[1];
} _OBJC_$_PROP_LIST_MyModel_$_Test __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_prop_t),
    1,
    {{"email","T@\"NSString\",C,N"}}
};

//添加关联引用
static void _I_MyModel_Test_setEmail_(MyModel * self, SEL _cmd, NSString * _Nonnull email) {
    objc_setAssociatedObject(self, (NSString *)&__NSConstantStringImpl__var_folders_lc_jzfd703x2r1czsk56zjspmw40000gn_T_MyModel_Test_00509d_mi_0,email, OBJC_ASSOCIATION_COPY);

}

//检索关联引用
static NSString * _Nonnull _I_MyModel_Test_email(MyModel * self, SEL _cmd) {

    return objc_getAssociatedObject(self, (NSString *)&__NSConstantStringImpl__var_folders_lc_jzfd703x2r1czsk56zjspmw40000gn_T_MyModel_Test_00509d_mi_1);
}


下面是MyModel的分类的结构体赋值,依次分析各个变量对应的赋值。

第1个是类名,将MyModel 赋值给了name

第2个cls,将0赋值给cls

第3个是对象列表,将_OBJC$CATEGORY_INSTANCE_METHODS_MyModel$_Test变量赋值给对象方法列表_instance_methods

第4个是类方法列表,将*_OBJC$CATEGORY_CLASS_METHODS_MyModel$_Test变量赋值给类方法列表class_methods

第5个协议列表,_OBJC_CATEGORY_PROTOCOLS$MyModel$_Test变量赋值给了protocols

最后是属性列表赋值,将_OBJC_$_PROP_LIST_MyModel_$_Test 变量赋值给了properties

static struct _category_t _OBJC_$_CATEGORY_MyModel_$_Test __attribute__ ((used, section ("__DATA,__objc_const"))) = 
{
    "MyModel",
    0, // &OBJC_CLASS_$_MyModel,
    (const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_MyModel_$_Test,
    (const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_MyModel_$_Test,
    (const struct _protocol_list_t *)&_OBJC_CATEGORY_PROTOCOLS_$_MyModel_$_Test,
    (const struct _prop_list_t *)&_OBJC_$_PROP_LIST_MyModel_$_Test,
};

从上面编译后的代码分析,我们可以看出,编译器生成了一个_category_t结构体,并给结构内定义的变量进行了赋值。_category_t结构体里面存储着分类的对象方法、类方法、协议、和属性,但是没有成员变量。这就是_category_t重写后的底层结构。

源码分析

Category运行时加载过程

_objc_init->map_images->map_images_nolock->_read_images->realizeClassWithoutSwift->methodizeClass->attachToClass

在上面的执行过程中,map_images方法只执行一次,这个过程加载并缓存所有Mach-O镜像文件,并没有进行合并分类的相关操作。

load_images->loadAllCategories->load_categories_nolock->attachCategories->attachLists

在这个过程中,才正式合并分类。需要注意的是,load_images只有在map_images执行结束后,才会执行,会执行多次。但是load_images内的loadAllCategories方法,只在分类还没初始化并且_dyld_objc_notify_register 的调用已经完成时,即didInitialAttachCategories 为false, didCallDyldNotifyRegister为true,此时才启用,此时就不再走realizeClassWithoutSwift->methodizeClass->attachToClass这一部分了,因为类对象已经存在了,因此改换成loadAllCategories后面的过程了。

_objc_init

首先在objc(objc4-818.2)开源库中, objc-os.mm类文件内,查找_objc_init 方法,在此方法内的_dyld_objc_notify_register函数注册了3个方法;我们只关注前面两个重要的函数参数;

map_images:加载并缓存所有Mach-O镜像文件;

load_images:初始化类和分类时需要;

unmap_image:取消内存映射。

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();
    tls_init();
    static_init();
    runtime_init();
    exception_init();
#if __OBJC2__
    cache_t::init();
#endif
    _imp_implementationWithBlock_init();
     //
    _dyld_objc_notify_register(&map_images, load_images, unmap_image);

#if __OBJC2__
    didCallDyldNotifyRegister = true;
#endif
}

map_images

作用:加载并缓存所有Mach-O镜像文件,主要操作在map_images_nolock方法中。程序启动后只执行一次。

void
map_images(unsigned count, const char * const paths[],
           const struct mach_header * const mhdrs[])
{
    mutex_locker_t lock(runtimeLock);
    return map_images_nolock(count, paths, mhdrs);
}

map_images_nolock

作用:对所有的镜像列表执行addHeader,主要过滤重复的镜像。 该方法内调用了加载所有Mach-O镜像文件的_read_images方法。

void map_images_nolock(unsigned mhCount, const char * const mhPaths[],
                  const struct mach_header * const mhdrs[])
{
     /**
     *代码过长,省略部分代码
     */

    if (hCount > 0) {
        _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
    }
}

_read_images

map_images方法调用过程中,最终会引用到 objc-runtime-new.mm 文件内的_read_images方法,_read_images方法的作用是从Mach-O镜像文件中读取所有类信息、方法信息、分类信息。

void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
{
    /**
    *省略了部分代码,只摘取重要代码
    */
 
    //搜索分类
    if (didInitialAttachCategories) {
        for (EACH_HEADER) {
            load_categories_nolock(hi);
        }
    }
    /**
     *当其他线程调用新分类代码之前,此线程完成其修复。分类搜索必须延迟以避免潜在的竞争。
     */
    for (EACH_HEADER) {
        classref_t const *classlist = hi->nlclslist(&count);
        for (i = 0; i < count; i++) {
            Class cls = remapClass(classlist[i]);
            if (!cls) continue;

            addClassTableEntry(cls);

            if (cls->isSwiftStable()) {
                if (cls->swiftMetadataInitializer()) {
                    _objc_fatal("Swift class %s with a metadata initializer "
                                "is not allowed to be non-lazy",
                                cls->nameForLogging());
                }
            }
          ////对类 cls 执行第一次初始化,不含swift端初始化
            realizeClassWithoutSwift(cls, nil);
        }
    }
}

realizeClassWithoutSwift

作用:实现ClassWithoutSwift,对类 cls 执行第一次初始化,包括分配其读写数据。不执行任何 Swift 端初始化。

返回类的真实类结构。锁定:runtimeLock 读写锁必须被调用者上写锁,保证线程安全。


static Class realizeClassWithoutSwift(Class cls, Class previously)
{
   runtimeLock.assertLocked();
    /**
    *省略了部分代码,只摘取重要代码
    */
  // Connect this class to its superclass's subclass lists
    if (supercls) {
        addSubclass(supercls, cls);
    } else {
        addRootClass(cls);
    }
     //附加分类
    // Attach categories
    methodizeClass(cls, previously);

    return cls;
}

methodizeClass

作用: 修正 cls 的方法列表、协议列表和属性列表。将 cls 类的所有没有被 attach 的分类 attach 到 cls 上。即将分类中的方法、属性、协议添加到 methods、 properties 和 protocols 中。锁定:runtimeLock 读写锁必须被调用者上写锁,保证线程安全。

static void methodizeClass(Class cls, Class previously)
{
    /**
    *省略了部分代码,只摘取重要代码
    */
 
    runtimeLock.assertLocked();
  
    // 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
     // 对根元类的initialize方法进行交换,即给根元类发送 SEL_initialize 消息,但是走的是 objc_noop_imp方法,里面不做任何操作
        addMethod(cls, @selector(initialize), (IMP)&objc_noop_imp, "", NO);
    }

    // Attach categories.
    //给 cls 类附加分类
    if (previously) {//如果已存在类
        if (isMeta) {//如果是元类,给cls附加分类,并返回cls 类的没有被附加的类
            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.
          //当一个类重新定位时,带有类方法的分类,可以在类自身而不是类之上的元类注册。通过attachToClass来添加这些。
            objc::unattachedCategories.attachToClass(cls, previously,
                                                     ATTACH_CLASS_AND_METACLASS);
        }
    }
    objc::unattachedCategories.attachToClass(cls, cls,
                                             isMeta ? ATTACH_METACLASS : ATTACH_CLASS);

}

load_images

作用: 当镜像的状态变化时,会回调load_images方法。只在map_images 结束后,分类没初始化时候启用。会多次调用。

extern bool hasLoadMethods(const headerType *mhdr);
extern void prepare_load_methods(const headerType *mhdr);

void
load_images(const char *path __unused, const struct mach_header *mh)
{
    if (!didInitialAttachCategories && didCallDyldNotifyRegister) {
        didInitialAttachCategories = true;
        loadAllCategories();
    }

    // Return without taking locks if there are no +load methods here.
    if (!hasLoadMethods((const headerType *)mh)) return;

    recursive_mutex_locker_t lock(loadMethodLock);

    // Discover load methods
    {
        mutex_locker_t lock2(runtimeLock);
        prepare_load_methods((const headerType *)mh);
    }

    // Call +load methods (without runtimeLock - re-entrant)
    call_load_methods();
}

loadAllCategories

作用:加载所有分类的入口

static void loadAllCategories() {
    mutex_locker_t lock(runtimeLock);

    for (auto *hi = FirstHeader; hi != NULL; hi = hi->getNext()) {
        load_categories_nolock(hi);
    }
}

load_categories_nolock

作用:加载分类的之前的区别判断。

static void load_categories_nolock(header_info *hi) {
    bool hasClassProperties = hi->info()->hasCategoryClassProperties();

    size_t count;
    auto processCatlist = [&](category_t * const *catlist) {
        for (unsigned i = 0; i < count; i++) {
            category_t *cat = catlist[i];
            Class cls = remapClass(cat->cls);
            locstamped_category_t lc{cat, hi};

            if (!cls) {
                // Category's target class is missing (probably weak-linked).
                // Ignore the category.
                if (PrintConnecting) {
                    _objc_inform("CLASS: IGNORING category \?\?\?(%s) %p with "
                                 "missing weak-linked target class",
                                 cat->name, cat);
                }
                continue;
            }

            // Process this category.
            if (cls->isStubClass()) {
                // Stub classes are never realized. Stub classes
                // don't know their metaclass until they're
                // initialized, so we have to add categories with
                // class methods or properties to the stub itself.
                // methodizeClass() will find them and add them to
                // the metaclass as appropriate.
                if (cat->instanceMethods ||
                    cat->protocols ||
                    cat->instanceProperties ||
                    cat->classMethods ||
                    cat->protocols ||
                    (hasClassProperties && cat->_classProperties))
                {
                    objc::unattachedCategories.addForClass(lc, cls);
                }
            } else {
                // First, register the category with its target class.
                // Then, rebuild the class's method lists (etc) if
                // the class is realized.
                if (cat->instanceMethods ||  cat->protocols
                    ||  cat->instanceProperties)
                {
                    //cls类已实现
                    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());
                    }
                }
            }
        }
    };

    processCatlist(hi->catlist(&count));
    processCatlist(hi->catlist2(&count));
}

attachCategories

作用:从分类列表中添加方法列表、属性和协议到 cls 类中, attachCategories 要求分类列表中是排好序的,新的在前面。

static void attachCategories(Class cls, const locstamped_category_t *cats_list, uint32_t cats_count,int flags)
{
  /**
    *省略了部分代码,只摘取重要代码
    */
    constexpr uint32_t ATTACH_BUFSIZ = 64;
    method_list_t   *mlists[ATTACH_BUFSIZ];
    property_list_t *proplists[ATTACH_BUFSIZ];
    protocol_list_t *protolists[ATTACH_BUFSIZ];

    uint32_t mcount = 0;
    uint32_t propcount = 0;
    uint32_t protocount = 0;
    bool fromBundle = NO;
    //判断是否为元类
    bool isMeta = (flags & ATTACH_METACLASS);
    auto rwe = cls->data()->extAllocIfNeeded();

    for (uint32_t i = 0; i < cats_count; i++) {
        //取出某个分类
        auto& entry = cats_list[i];
       //方法数组 ,如果是元类,返回的是类方法,如果是类,则返回是实例对象方法
        method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
        if (mlist) {
            if (mcount == ATTACH_BUFSIZ) {
              //准备 mlists 中的方法列表集合,mcount:列表个数,NO:排除基本方法,fromBundle:是否来自bundle
                prepareMethodLists(cls, mlists, mcount, NO, fromBundle, __func__);
              // 将新mlist列表添加到 rwe 中的方法列表数组中
                rwe->methods.attachLists(mlists, mcount);
                mcount = 0;
            }
            mlists[ATTACH_BUFSIZ - ++mcount] = mlist;
            fromBundle |= entry.hi->isBundle();
        }
       //属性数组
        property_list_t *proplist =
            entry.cat->propertiesForMeta(isMeta, entry.hi);
        if (proplist) {
            if (propcount == ATTACH_BUFSIZ) {
              // 将新proplist列表添加到 rwe 中的属性列表数组中
                rwe->properties.attachLists(proplists, propcount);
                propcount = 0;
            }
            proplists[ATTACH_BUFSIZ - ++propcount] = proplist;
        }
        //协议数组
        protocol_list_t *protolist = entry.cat->protocolsForMeta(isMeta);
        if (protolist) {
            if (protocount == ATTACH_BUFSIZ) {
              // 将新protolist列表添加到 rwe 中的协议列表数组中
                rwe->protocols.attachLists(protolists, protocount);
                protocount = 0;
            }
            protolists[ATTACH_BUFSIZ - ++protocount] = protolist;
        }
    }

    if (mcount > 0) {
        prepareMethodLists(cls, mlists + ATTACH_BUFSIZ - mcount, mcount,
                           NO, fromBundle, __func__);
        rwe->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount);
        if (flags & ATTACH_EXISTING) {
            flushCaches(cls, __func__, [](Class c){
                // constant caches have been dealt with in prepareMethodLists
                // if the class still is constant here, it's fine to keep
                return !c->cache.isConstantOptimizedCache();
            });
        }
    }

    rwe->properties.attachLists(proplists + ATTACH_BUFSIZ - propcount, propcount);

    rwe->protocols.attachLists(protolists + ATTACH_BUFSIZ - protocount, protocount);
}


attachLists

作用:将新的list数据与原有的list 数据进行合并。

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

    void tryFree() {
        if (hasArray()) {
            for (uint32_t i = 0; i < array()->count; i++) {
                try_free(array()->lists[i]);
            }
            try_free(array());
        }
        else if (list) {
            try_free(list);
        }
    }

    template<typename Other>
    void duplicateInto(Other &other) {
        if (hasArray()) {
            array_t *a = array();
            other.setArray((array_t *)memdup(a, a->byteSize()));
            for (uint32_t i = 0; i < a->count; i++) {
                other.array()->lists[i] = a->lists[i]->duplicate();
            }
        } else if (list) {
            other.list = list->duplicate();
        } else {
            other.list = nil;
        }
    }
};

Category之 load

1、先编译的类,优先调用load

2、先调用父类的load,再调用子类的load

3、先调用类load,再调用分类load,然后先编译的分类先调用load

4、调用时机最靠前,在main函数运行之前,load 方法就会调用,适合方法交换。

5、不是懒加载,只会在程序调用期间调用一次,谨记,若在类与分类中都实现了 load 方法,两者都会被调用,方法不会被覆盖,但是顺序不确定。

Category之 initialize

1、若是子类,会先走父类initialize,再走子类initialize,如果子类没有重写这个方法,父类里这个方法也会被调用;

2、initialize 是类第一次接收到消息的时候调用,每一个类只会initialize一次,父类可能不止调用一次;

3、和 load 不同,initialize 方法调用时,所有的类都load到了内存中;

4、initialize 的运行线程安全,一般只在 initialize中进行常量的初始化 。

总结:

Category运行时的加载过程,是通过动态的先初始化类和元类的相关信息,然后再将分类的实例方法、类方法、属性以及协议,合并到类对象和元类对象中。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章