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成员变量结构体和setter和getter方法,这也证明了分类不可以添加成员变量。而属性的setter和getter 方法,编译后的是走的我们手动添加的相关的关联引用的方法。
//协议
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运行时的加载过程,是通过动态的先初始化类和元类的相关信息,然后再将分类的实例方法、类方法、属性以及协议,合并到类对象和元类对象中。