Runtime02KVO和Category原理

遍历监听对象前后的实例方法的变化:

界面效果就是一个空白界面:
在这里插入图片描述

上代码:

Person类:

@interface Person : NSObject
{
@public
    int _age;
}
//系统会默认为age生成set和get方法
@property (nonatomic, assign) int age;
@end
//---------分割线,分隔一个类的.h文件和.m文件-------
#import "Person.h"
@implementation Person
@end

ViewController类:

#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@end
//---------分割线,分隔一个类的.h文件和.m文件-------
#import "ViewController.h"
#import "Person.h"
#import <objc/runtime.h>

@interface ViewController ()
@property (strong, nonatomic) Person *person1;
@property (strong, nonatomic) Person *person2;
@end

@implementation ViewController
- (void)dealloc {
    [self.person1 removeObserver:self forKeyPath:@"age"];
}

/*
 既然下面的viewDidLoad()已经添加了对person1的age属性的监听。所以runtime机制会在运行时生成Person的子类,该子类名为NSKVONotifying_Person,所以此时就不要在你的项目里面再创建名为NSKVONotifying_Person的类了!如果你手动在工程添加了名为NSKVONotifying_Person的类,那么运行时会报 KVO failed to allocate class pair for name NSKVONotifying_Person, automatic key-value observing will not work for this class的警告,即runtime机制不会在运行时自动创建NSKVONotifying_Person类了,进而导致person1的age属性的监听会失效。
 
 */
- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.person1 = [[Person alloc] init];
    self.person1.age = 1;
    
    self.person2 = [[Person alloc] init];
    self.person2.age = 2;

    // 给person1对象添加对Person的age属性的监听
    NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
    [self.person1 addObserver:self forKeyPath:@"age" options:options context:@"123"];

	NSLog(@"object_getClass(self.person1) = %@, [person1 class = %@]", object_getClass(self.person1),
          [self.person1 class]);
    NSLog(@"object_getClass(self.person2) = %@, [person2 class = %@]", object_getClass(self.person2),
          [self.person2 class]);
    
    
    NSLog(@"--------self.person1的方法有:");
    [self printMethodNamesOfClass:object_getClass(self.person1)];
    NSLog(@"--------self.person2的方法有:");
    [self printMethodNamesOfClass:object_getClass(self.person2)];
    
}

- (void)printMethodNamesOfClass:(Class)cls {  //获取的是在Person.m文件或者系统自动为属性生成的get和set方法
    unsigned int count;
    //获得方法数组
    Method *methodList = class_copyMethodList(cls, &count);
    //存储方法名
    NSMutableString *methodNames = [NSMutableString string];
    //遍历所有的方法
    for (int i = 0; i < count; i++) {
        //获得方法
        Method method = methodList[i];
        //获得方法名
        NSString *methodName = NSStringFromSelector(method_getName(method));
        //拼接方法名
        [methodNames appendString:methodName];
        [methodNames appendString:@", "];
    }
    //释放内存
    free(methodList);
    //打印方法名
    NSLog(@"class = %@,方法有: %@", cls, methodNames);
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
//    self.person1.age = 11; //会调用KVO,即会调用person1的setAge()
    
//    self.person1->_age = 11; //报错:'Person' does not have a member named '_age'
    
//    [self.person1 setValue:@123 forKey:@"_age"]; //不会调用KVO,即不会调用person1的setAge(),是因为直接赋值给成员变量_age
    
    [self.person1 setValue:@1234 forKey:@"age"]; //会调用KVO,即会调用person1的setAge()
    
//    [self.person1 willChangeValueForKey:@"age"];
//    [self.person1 didChangeValueForKey:@"age"]; //这一行代码里面最终会调用Observer(本例中指ViewController)的observeValueForKeyPath方法
    
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    NSLog(@"监听到的%@ 的 %@属性值改变了,存放新旧值的字典为%@, context = %@ ", object, keyPath, change, context);
}

@end

在屏幕上滑动一下的调用栈如下

在这里插入图片描述

上图的打印结果分析

添加对Person类的age属性的监听后,runtime机制会在运行时生成一个名为NSKVONitifying_Person的类,并实现(重写)了setAge()、class()、dealloc()、_isKVOA()方法。其中,重写的setAge()的实现伪代码如下:

- (void)setAge:(int)age {
    _NSSetIntValueAndNotify(); //伪代码
}

//伪代码
void _NSSetIntValueAndNotify() {
    [self willChangeValueForKey:@"age"];
    [super setAge:age];
    [self didChangeValueForKey:@"age"];
}

//伪代码
- (void)didChangeValueForKey:(NSString *)key {
    //通知监听器,某某属性值发送了改变
    [oberser observeValueForKeyPath:key ofObject:self change:nil context:nil];
}

重写的class()的实现的伪代码如下:

Class class {
	return [super class];
}

分类(Category)

先看代码

main.m代码

#import <Foundation/Foundation.h>
#import "Person+Eat.h"
#import "Person+Test.h"
#import "Person.h"
/**
 每一个类的分类在编译时都会由一个_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;
     };
 
 在编译时,就已经确定了每一个类的分类都是_category_t结构体的一个实例。
 */
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *person = [Person new];
        [person run];
        
        objc_msgSend(person, @selector(run));
        
        [person test];
        
        [person eat];
        
        objc_msgSend(person, @selector(eat));
        
        //通过runtime机制动态的将分类的方法合并到类对象、元类对象中
    }
    return 0;
}

Person类:

@interface Person : NSObject
- (void)run;
@end
//---------分割线,分隔一个类的.h文件和.m文件-------
#import "Person.h"
@interface Person() {
    int _abc;
}
@property (nonatomic, assign)int age;
- (void)abc;

@end

@implementation Person

- (void)abc {
    
}

- (void)run {
    NSLog(@"Person run");
}

+ (void)run2 { 
}

@end

Person+Test 分类的代码如下:

@interface Person (Test)
- (void)test;
@end
//---------分割线,分隔一个类的.h文件和.m文件-------
#import "Person+Test.h"

@implementation Person (Test)

- (void)run {
    NSLog(@"Person (Test) - run");
}

- (void)test {
    NSLog(@"Person (Test) test");
}

+ (void)test2 {
}

@end

Person+Eat 分类的代码如下

#import "Person.h"

@interface Person (Eat)<NSCopying, NSCoding>
- (void)eat;
@property (assign, nonatomic) int weight;
@property (assign, nonatomic) int height;
@end
//---------分割线,分隔一个类的.h文件和.m文件-------
#import "Person+Eat.h"

@implementation Person  (Eat)

- (void)run {
    NSLog(@"Person (Eat) - run");
}

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

+ (void)eat2
{
}

@end

上述代码可能因为objc_msgSend()而导致编译不过,此时需要关闭对objc_msgSend的检查,具体操作如下:
在这里插入图片描述
调整.m文件编译的顺序,如下图所示
在这里插入图片描述

点击运行,打印结果如下:
在这里插入图片描述

打印结果分析

因为Person+Test
前置知识

Category里面的对象方法(- 号开头的方法)只有在运行时(runtime)才会被添加到类的方法列表中,Category里面的类方法(+ 号开头的方法)只有在运行时(runtime)才会被添加到类的元类的方法列表中,而不是在编译时被添加。整个编译过程中,编译器先将oc代码转为C++代码(即把.m文件转成.cpp文件,通过执行xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc xxxxx.m 命令实现转化),然后编译成可执行代码,然后运行,在运行的过程中(runtime),系统才会把分类所实现的实例方法和类方法分别加入到类对象的实例方法列表和类方法列表中。

以上面的例子为例,在Person+Eat.m文件的目录下,执行xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc Person+Eat.m,系统就会生成Person+Eat.cpp。该Person+Eat.cpp文件的部分关键代码如下:

struct _prop_t { //代表属性的结构体
	const char *name;
	const char *attributes;
};

struct _protocol_t;
struct _objc_method {  //代表方法(Method)的结构体
	struct objc_selector * _cmd;
	const char *method_type;
	void  *_imp;
};

struct _protocol_t {  //代表协议的结构体
	void * isa;  // NULL
	const char *protocol_name;
	const struct _protocol_list_t * protocol_list; // super protocols
	const struct method_list_t *instance_methods;
	const struct method_list_t *class_methods;
	const struct method_list_t *optionalInstanceMethods;
	const struct method_list_t *optionalClassMethods;
	const struct _prop_list_t * properties;
	const unsigned int size;  // sizeof(struct _protocol_t)
	const unsigned int flags;  // = 0
	const char ** extendedMethodTypes;
};

struct _ivar_t {  //代表成员变量的结构体
	unsigned long int *offset;  // pointer to ivar offset location
	const char *name;
	const char *type;
	unsigned int alignment;
	unsigned int  size;
};

struct _class_ro_t { //class_readOnly_table的缩写
	unsigned int flags;
	unsigned int instanceStart;
	unsigned int instanceSize;
	const unsigned char *ivarLayout;
	const char *name;  //类名
	const struct _method_list_t *baseMethods; 
	const struct _objc_protocol_list *baseProtocols;
	const struct _ivar_list_t *ivars;  //成员变量列表,类的成员变量都放在这里
	const unsigned char *weakIvarLayout;
	const struct _prop_list_t *properties;  //属性列表
};

struct _class_t {  //表示一个类的结构体
	struct _class_t *isa;
	struct _class_t *superclass;
	void *cache;
	void *vtable;
	struct _class_ro_t *ro;
};

struct _category_t {  //每一个类的分类在编译时都会由一个_category_t结构体表示,即每一个分类都是_category_t结构体的一个实例。不同的分类的信息肯定不同,所以不同的_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; //属性列表
};
extern "C" __declspec(dllimport) struct objc_cache _objc_empty_cache;
#pragma warning(disable:4273)

static struct /*_method_list_t*/ {  //Person+Eat分类的实例方法列表
	unsigned int entsize;  // sizeof(struct _objc_method)
	unsigned int method_count;
	struct _objc_method method_list[2]; //Person+Eat分类的实例方法有2个,分别为run()和eat()方法
} _OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_Eat __attribute__ ((used, section ("__DATA,__objc_const"))) = {
	sizeof(_objc_method),
	2,
	{{(struct objc_selector *)"run", "v16@0:8", (void *)_I_Person_Eat_run},
	{(struct objc_selector *)"eat", "v16@0:8", (void *)_I_Person_Eat_eat}}
};

static struct /*_method_list_t*/ { //Person+Eat分类的类方法列表
	unsigned int entsize;  // sizeof(struct _objc_method)
	unsigned int method_count;
	struct _objc_method method_list[1];  //Person+Eat分类的类方法有1个,即eat2()方法
} _OBJC_$_CATEGORY_CLASS_METHODS_Person_$_Eat __attribute__ ((used, section ("__DATA,__objc_const"))) = {
	sizeof(_objc_method),
	1,
	{{(struct objc_selector *)"eat2", "v16@0:8", (void *)_C_Person_Eat_eat2}}
};

static const char *_OBJC_PROTOCOL_METHOD_TYPES_NSCopying [] __attribute__ ((used, section ("__DATA,__objc_const"))) = 
{
	"@24@0:8^{_NSZone=}16"
};

static struct /*_method_list_t*/ {   //Person+Eat分类遵守的的NSCopying协议的实例方法列表
	unsigned int entsize;  // sizeof(struct _objc_method)
	unsigned int method_count;
	struct _objc_method method_list[1]; //实例方法列表有1个copyWithZone()方法
} _OBJC_PROTOCOL_INSTANCE_METHODS_NSCopying __attribute__ ((used, section ("__DATA,__objc_const"))) = {
	sizeof(_objc_method),
	1,
	{{(struct objc_selector *)"copyWithZone:", "@24@0:8^{_NSZone=}16", 0}}
};

struct _protocol_t _OBJC_PROTOCOL_NSCopying __attribute__ ((used)) = {
	0,
	"NSCopying",
	0,
	(const struct method_list_t *)&_OBJC_PROTOCOL_INSTANCE_METHODS_NSCopying,
	0,
	0,
	0,
	0,
	sizeof(_protocol_t),
	0,
	(const char **)&_OBJC_PROTOCOL_METHOD_TYPES_NSCopying
};
struct _protocol_t *_OBJC_LABEL_PROTOCOL_$_NSCopying = &_OBJC_PROTOCOL_NSCopying;

static const char *_OBJC_PROTOCOL_METHOD_TYPES_NSCoding [] __attribute__ ((used, section ("__DATA,__objc_const"))) = 
{
	"v24@0:8@\"NSCoder\"16",
	"@24@0:8@\"NSCoder\"16"
};

static struct /*_method_list_t*/ { //Person+Eat分类遵守的的NSCoding协议的实例方法列表,有2个实例方法
	unsigned int entsize;  // sizeof(struct _objc_method)
	unsigned int method_count;
	struct _objc_method method_list[2];
} _OBJC_PROTOCOL_INSTANCE_METHODS_NSCoding __attribute__ ((used, section ("__DATA,__objc_const"))) = {
	sizeof(_objc_method),
	2,
	{{(struct objc_selector *)"encodeWithCoder:", "v24@0:8@16", 0},
	{(struct objc_selector *)"initWithCoder:", "@24@0:8@16", 0}}
};

struct _protocol_t _OBJC_PROTOCOL_NSCoding __attribute__ ((used)) = {
	0,
	"NSCoding",
	0,
	(const struct method_list_t *)&_OBJC_PROTOCOL_INSTANCE_METHODS_NSCoding,
	0,
	0,
	0,
	0,
	sizeof(_protocol_t),
	0,
	(const char **)&_OBJC_PROTOCOL_METHOD_TYPES_NSCoding
};
struct _protocol_t *_OBJC_LABEL_PROTOCOL_$_NSCoding = &_OBJC_PROTOCOL_NSCoding;

static struct /*_protocol_list_t*/ {  //Person+Eat分类所遵守的协议方法列表
	long protocol_count;  // Note, this is 32/64 bit
	struct _protocol_t *super_protocols[2];
} _OBJC_CATEGORY_PROTOCOLS_$_Person_$_Eat __attribute__ ((used, section ("__DATA,__objc_const"))) = {
	2,
	&_OBJC_PROTOCOL_NSCopying,
	&_OBJC_PROTOCOL_NSCoding
};

static struct /*_prop_list_t*/ {  //Person+Eat分类的属性方法列表
	unsigned int entsize;  // sizeof(struct _prop_t)
	unsigned int count_of_properties;
	struct _prop_t prop_list[2];
} _OBJC_$_PROP_LIST_Person_$_Eat __attribute__ ((used, section ("__DATA,__objc_const"))) = {
	sizeof(_prop_t),
	2,
	{{"weight","Ti,N"},
	{"height","Ti,N"}}
};

extern "C" __declspec(dllimport) struct _class_t OBJC_CLASS_$_Person;

//每一个分类都是_category_t结构体的一个实例,下面的_category_t结构体的实例存储的是Person+Eat分类的信息。
static struct _category_t _OBJC_$_CATEGORY_Person_$_Eat __attribute__ ((used, section ("__DATA,__objc_const"))) = 
{
	"Person", //Person+Eat分类的类名
	0, // &OBJC_CLASS_$_Person, 
	(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_Eat, //Person+Eat分类的实例方法列表
	(const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_Person_$_Eat, //Person+Eat分类的类方法列表
	(const struct _protocol_list_t *)&_OBJC_CATEGORY_PROTOCOLS_$_Person_$_Eat, //Person+Eat分类的协议列表
	(const struct _prop_list_t *)&_OBJC_$_PROP_LIST_Person_$_Eat, //Person+Eat分类的属性列表
};

在Person+Test.m文件的目录下,执行xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc Person+Test.m,系统就会生成Person+Test.cpp。该Person+Test.cpp文件的部分关键代码如下:

struct _category_t {  //每一个类的分类在编译时都会由一个_category_t结构体表示,即每一个分类都是_category_t结构体的一个实例。不同的分类的信息肯定不同,所以不同的_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;
};
extern "C" __declspec(dllimport) struct objc_cache _objc_empty_cache;
#pragma warning(disable:4273)

static struct /*_method_list_t*/ { //Person+Test分类的实例方法列表
	unsigned int entsize;  // sizeof(struct _objc_method)
	unsigned int method_count;
	struct _objc_method method_list[2];
} _OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_Test __attribute__ ((used, section ("__DATA,__objc_const"))) = {
	sizeof(_objc_method),
	2,
	{{(struct objc_selector *)"run", "v16@0:8", (void *)_I_Person_Test_run},
	{(struct objc_selector *)"test", "v16@0:8", (void *)_I_Person_Test_test}}
};

static struct /*_method_list_t*/ { //Person+Test分类的类方法列表
	unsigned int entsize;  // sizeof(struct _objc_method)
	unsigned int method_count;
	struct _objc_method method_list[1];
} _OBJC_$_CATEGORY_CLASS_METHODS_Person_$_Test __attribute__ ((used, section ("__DATA,__objc_const"))) = {
	sizeof(_objc_method),
	1,
	{{(struct objc_selector *)"test2", "v16@0:8", (void *)_C_Person_Test_test2}}
};

extern "C" __declspec(dllimport) struct _class_t OBJC_CLASS_$_Person;
//每一个分类都是_category_t结构体的一个实例,下面的_category_t结构体的实例存储的是Person+Test分类的信息。
static struct _category_t _OBJC_$_CATEGORY_Person_$_Test __attribute__ ((used, section ("__DATA,__objc_const"))) = 
{
	"Person", //Person+Test分类所属的类的类名
	0, // &OBJC_CLASS_$_Person,
	(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_Test, //Person+Test分类的实例方法列表
	(const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_Person_$_Test, //Person+Test分类的类方法列表
	0,
	0,
};

接下来分析runtime源码,runtime源码中定义的 category_t 结构体 和 上面将分类的.m文件转化成.cpp文件中定义的 _category_t 结构体差别不大。
runtime里面定义的category_t 的定义如下:

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

当一个程序被加载进内存并开始运行时,方法的调用顺序的时序图如下
在这里插入图片描述
上图的runtime源码的方法调用如下
程序被加载进内存并开始运行时,调用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();
    tls_init();
    static_init();
    lock_init();
    exception_init();

    _dyld_objc_notify_register(&map_images, load_images, unmap_image); //这行是关键,map_images是一个函数名
}

map_images()函数

/***********************************************************************
* map_images
* Process the given images which are being mapped in by dyld.
* Calls ABI-agnostic code after taking ABI-specific locks.
*
* Locking: write-locks runtimeLock
**********************************************************************/
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); //看这行,images指的是镜像、模块,该方法会遍历类的所有分类(每个分类的信息都由一个category_t结构体的实例存储)
}

map_images_nolock()函数部分代码如下

/***********************************************************************
* map_images_nolock
* Process the given images which are being mapped in by dyld.
* All class registration and fixups are performed (or deferred pending
* discovery of missing superclasses etc), and +load methods are called.
*
* info[] is in bottom-up order i.e. libobjc will be earlier in the 
* array than any library that links to libobjc.
*
* Locking: loadMethodLock(old) or runtimeLock(new) acquired by map_images.
**********************************************************************/
#if __OBJC2__
#include "objc-file.h"
#else
#include "objc-file-old.h"
#endif

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()函数如下

/***********************************************************************
* _read_images
* Perform initial processing of the headers in the linked 
* list beginning with headerList. 
*
* Called by: map_images_nolock
*
* Locking: runtimeLock acquired by map_images
**********************************************************************/
void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
{
    header_info *hi;
    uint32_t hIndex;
    size_t count;
    size_t i;


#define EACH_HEADER \
    hIndex = 0;         \
    hIndex < hCount && (hi = hList[hIndex]); \
    hIndex++

    // 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);
           
            // Process this category. 
            // First, register the category with its target class. 
            // Then, rebuild the class's method lists (etc) if 
            // the class is realized. 
            bool classExists = NO;
            if (cat->instanceMethods ||  cat->protocols  
                ||  cat->instanceProperties) 
            {
                addUnattachedCategoryForClass(cat, cls, hi);
                if (cls->isRealized()) {
                    remethodizeClass(cls);  //把实例方法、实例属性和协议都分别添加到类方法列表、属性列表和协议列表中
                    classExists = YES;
                }
            }

            if (cat->classMethods  ||  cat->protocols  
                ||  (hasClassProperties && cat->_classProperties)) 
            {
                addUnattachedCategoryForClass(cat, cls->ISA(), hi);
                if (cls->ISA()->isRealized()) {
                    remethodizeClass(cls->ISA());  //把类方法、类属性和协议都分别添加到类的元类的方法列表、属性列表和协议列表中
                }
            }
        }
    }
#undef EACH_HEADER
}

remethodizeClass()代码如下:

/***********************************************************************
* remethodizeClass
* Attach outstanding categories to an existing class.
* Fixes up cls's method list, protocol list, and property list.
* Updates method caches for cls and its subclasses.
* Locking: runtimeLock must be held by the caller
**********************************************************************/
static void remethodizeClass(Class cls)
{
    category_list *cats;
    // Re-methodizing: check for more categories
    if ((cats = unattachedCategoriesForClass(cls, false/*not realizing*/))) {

        attachCategories(cls, cats, true /*flush caches*/);        //把分类的信息添加到到所属的类中
        free(cats);
    }
}
//看注释
// Attach method lists and properties and protocols from categories to a class.
// Assumes the categories in cats are all loaded and sorted by load order, 
// oldest categories first.
static void attachCategories(Class cls, category_list *cats, bool flush_caches)
{
    if (!cats) return;
    bool isMeta = cls->isMetaClass(); //cls是否是元类

    // fixme rearrange to remove these intermediate allocations
    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;
    while (i--) { //从后往前遍历类的所有分类
        auto& entry = cats->list[i];

        method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
        if (mlist) {
            mlists[mcount++] = mlist;
            fromBundle |= entry.hi->isBundle();
        }

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

    auto rw = cls->data(); //rw是class_rw_t类型,class_rw_t存储了类的方法列表、属性列表、协议列表、第一个子类的地址、一个class_ro_t类型的指针。class_ro_t存储了实例对象的占用的内存空间大小、类名、成员变量列表

    prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
    rw->methods.attachLists(mlists, mcount);  //把分类的实例方法列表(或者类方法列表)添加到类的方法列表中(或者元类的方法列表中)
    free(mlists);
    if (flush_caches  &&  mcount > 0) flushCaches(cls);

    rw->properties.attachLists(proplists, propcount);//把分类的实例属性列表(或者类属性列表)添加到类的属性列表中(或者元类的属性列表中)
    free(proplists);

    rw->protocols.attachLists(protolists, protocount);//把分类遵守的协议列表添加到类的协议列表中(或者元类的协议列表中)
    free(protolists);
}

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;
            //array()->lists存的是类的原来的方法
            memmove(array()->lists + addedCount, array()->lists, 
                    oldCount * sizeof(array()->lists[0])); //把类的方法列表(说白了就是个数组)中原有的方法往后移addedCount个位置,其中addedCount指的是要添加的方法个数。
           
            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]));
        }
}

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值