序言
- Objective-C是一门动态性比较强的编程语言,跟C、C++等语言有着很大的不同
- Objective-C的动态性是由Runtime API来支撑的
- Runtime API提供的接口基本都是C语言的,源码由C\C++\汇编语言编写
Class的结构

class_rw_t
class_rw_t里面的methods、properties、protocols是二维数组,是可读可写的,包含了类的初始内容、分类的内容

class_ro_t
class_ro_t里面的baseMethodList、baseProtocols、ivars、baseProperties是一维数组,是只读的,包含了类的初始内容

method_t
method_t是对方法\函数的封装
struct method_t {
SEL name; // 函数名
const char *types; // 编码 (返回值类型,参数类型)
IMP imp; // 指向函数的指针(函数地址)
};
1. SEL
代表方法\函数名,一般叫做选择器,底层结构跟char *类似
- 可以通过
@selector()
和sel_registerName()
获得 - 可以通过
sel_getName()
和NSStringFromSelector()
转成字符串 - 不同类中相同名字的方法,所对应的方法选择器是相同的
typedef struct objc_selector *SEL;
2. types
包含了函数返回值、参数编码的字符串

3. IMP
代表函数的具体实现

4. Type Encoding
iOS中提供了一个叫做@encode的指令,可以将具体的类型表示成字符串编码

-
1.SEL
代码例子如下
// 1.SEL
SEL sel1 = sel_registerName("test");
SEL sel2 = @selector(test);
NSLog(@"%p %p %p",@selector(test),@selector(test),sel1);
运行结果如下

SEL只是一个方法字符串,和属于哪个类,或者通过runtime获取都没有关系,他们的内存地址都是一样的
2.IMP代码例子如下
@interface Person : NSObject
- (void)test;
@end
@implementation Person
- (void)test {
NSLog(@"%s",__func__);
}
@end
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import "Person.h"
#import "MJClassInfo.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [[Person alloc] init];
mj_objc_class *cls = (__bridge mj_objc_class *)[Person class];
class_rw_t *data = cls->data();
// 2.IMP
[person test];
NSLog(@"end");
}
return 0;
}


可以知道IMP指向类的第一个方法地址
-
3.types
代码例子如下
@interface Person : NSObject
- (void)test;
// "i24@0:8i16f20"
// 0id 8SEL 16int 20float == 24
- (int)test:(int)age height:(float)height;
@end
@implementation Person
- (void)test {
NSLog(@"%s",__func__);
}
- (int)test:(int)age height:(float)height {
return age + height;
}
@end
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import "Person.h"
#import "MJClassInfo.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [[Person alloc] init];
mj_objc_class *cls = (__bridge mj_objc_class *)[Person class];
class_rw_t *data = cls->data();
// 3.types
[person test:100 height:100];
NSLog(@"end");
}
return 0;
}
运行结果如下

"i24@0:8i16f20"
0id 8SEL 16int 20float == 24
- (int)test:(int)age height:(float)height;
三 方法缓存
Class内部结构中有个方法缓存(cache_t),用散列表(哈希表)
来缓存曾经调用过的方法,可以提高方法的查找速度


-
_mask
的长度代码例子佐证
分别建三个类并且有继承关系GoodStudent -> Student -> Person,并且内部都有一个打印方法,详情见项目连接地址
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Student.h"
#import "GoodStudent.h"
#import "MJClassInfo.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
GoodStudent *goodStudent = [[GoodStudent alloc] init];
mj_objc_class *goodStudentClass = (__bridge mj_objc_class *)[GoodStudent class];
[goodStudent goodStudentTest];
[goodStudent studentTest];
[goodStudent personTest];
NSLog(@"--------------------------");
}
return 0;
}
打印结果




-
2.bucket
的佐证
int main(int argc, const char * argv[]) {
@autoreleasepool {
GoodStudent *goodStudent = [[GoodStudent alloc] init];
mj_objc_class *goodStudentClass = (__bridge mj_objc_class *)[GoodStudent class];
// 1. _mask的数量
[goodStudent goodStudentTest];
[goodStudent studentTest];
[goodStudent personTest];
[goodStudent goodStudentTest];
[goodStudent studentTest];
[goodStudent personTest];
NSLog(@"--------------------------");
// 2.打印bucket_t的值
cache_t cache = goodStudentClass->cache;
bucket_t *buckets = cache._buckets;
for (int i = 0; i <= cache._mask; i++) {
bucket_t bucket = buckets[i];
NSLog(@"%s %p", bucket._key, bucket._imp);
}
}
return 0;
}
打印结果


-
3.cache.imp
通过哈希表找对应方法佐证
int main(int argc, const char * argv[]) {
@autoreleasepool {
GoodStudent *goodStudent = [[GoodStudent alloc] init];
mj_objc_class *goodStudentClass = (__bridge mj_objc_class *)[GoodStudent class];
// 1. _mask的数量
[goodStudent goodStudentTest];
[goodStudent studentTest];
[goodStudent personTest];
[goodStudent goodStudentTest];
[goodStudent studentTest];
[goodStudent personTest];
NSLog(@"--------------------------");
// 3.通过cache.imp找到方法
cache_t cache = goodStudentClass->cache;
NSLog(@"%s %p", @selector(personTest), cache.imp(@selector(personTest)));
NSLog(@"%s %p", @selector(studentTest), cache.imp(@selector(studentTest)));
NSLog(@"%s %p", @selector(goodStudentTest), cache.imp(@selector(goodStudentTest)));
NSLog(@"end");
}
return 0;
}

缓存查找
- objc-cache.mm
- bucket_t * cache_t::find(cache_key_t k, id receiver)
本文主要参考MJ老师的教案,非常感谢MJ老师。
项目连接地址
runtime-method详解
runtime-cache
关于runtime更多文章请看如下链接
iOS-runtime-API详解+使用
iOS Runtime原理及使用
iOS - runtime如何通过selector找到对应的 IMP地址(分别考虑类方法和实例方法)
iOS - Runtime之面试题详解一
iOS-runtime之面试题详解二
iOS runtime的使用场景-实战篇
网友评论