runtime 接口说明

文章目录
  1. 1. Message
    1. 1.1. objc_msgSend 消息机制
    2. 1.2. methodForSelector 获取消息实现地址 IMP,直接调用方法
    3. 1.3. 动态方法解析, class_addMethod 动态添加方法
    4. 1.4. forwardInvocation 消息转发
  2. 2. 属性
    1. 2.1. 打印 property,attribute
    2. 2.2. 实现字典转模型
  3. 3. 创建类&元类
  4. 4. 方法
    1. 4.1. method_exchangeImplementations 方法交换
    2. 4.2. 通过方法交换 class_replaceMethod Hook 指定方法

Message

objc_msgSend 消息机制

  1. 发送消息,继承结构查询消息
  2. 动态方法解析,让用户自己给实例对象 or 类对象添加方法
  3. 消息转发,当 1,2 都失败以后,会通过 NSInvocation 的方式将消息转发给其他对象
1
2
3
4
5
6
7
8
9
10
11
12
13
@import ObjectiveC.message;

@implementation MessageObject
- (void)message:(int)number{
printf("%d\n", number);
}
@end

// 消息机制,都会在编译后转换成 objc_msgSend 方法调用,内部执行runtime 消息机制
// 注意 objc_msgSend 类型转换
MessageObject *receiver = [MessageObject new];
//objc_msgSend 使用的时候需要 强转下方法类型 (void(*)(id, SEL, int))
((void(*)(id, SEL, int))objc_msgSend)(receiver, @selector(message:), 4);

methodForSelector 获取消息实现地址 IMP,直接调用方法

在 for 循环中,如果反复对一个对象发送消息每次都会调用 objc_msgSend
第一次会执行 objc_msgSend 全流程,知道找到方法
第二次以后都会从 对象 cache_t 中找到函数地址 IMP
如果确定发送消息的具体实现可以通过消息选择器得到 函数地址 IMP 直接调用

1
2
IMP message = [receiver  methodForSelector:@selector(message:)];
((void(*)(id, SEL, int))message)(receiver, @selector(message:), 5);

动态方法解析, class_addMethod 动态添加方法

消息机制第一步对象继承图中没有找到对应消息,那么动态解析添加方法
class_addMethod

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@import ObjectiveC.runtime;

static void dynamicInstanceMethodIMP(id self, SEL _cmd) {
printf("instance resolved\n");
}

static void dynamicClassMethodIMP(id self, SEL _cmd) {
printf("class resolved\n");
}

@implementation DynamicResolve

+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(resolveThisMethodDynamically)) {
// 给类添加方法
class_addMethod([self class], sel, (IMP) dynamicInstanceMethodIMP, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}

+ (BOOL)resolveClassMethod:(SEL)sel {
if (sel == @selector(resolveThisMethodDynamically)) {
// 给元类添加方法
class_addMethod(objc_getMetaClass("DynamicResolve"), sel, (IMP) dynamicClassMethodIMP, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
@end

forwardInvocation 消息转发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@implementation MessageForwarding

// 消息转发过程中必须实现这个方法,找到消息的唯一身份签名!
- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector {
printf("origin:%s\n", "MessageForwarding");
NSMethodSignature* signature = [super methodSignatureForSelector:selector];
if (!signature) {
signature = [[ForwardObject new] methodSignatureForSelector:selector];
}
return signature;
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
ForwardObject *obj = [ForwardObject new];
if ([obj respondsToSelector:[anInvocation selector]])
[anInvocation invokeWithTarget:obj];
else
[super forwardInvocation:anInvocation];
}
@end

属性

class_addIvar 这个方法只能在 objc_allocateClassPair 后,objc_registerClassPair 前被调用, 不可以给已加载的类添加变量
因为添加 ivar 会改变 class 的layout,通过关联对象给class 添加属性

打印 property,attribute

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@interface Lender : NSObject {
float alone;
}
@property float alone;
@end

id LenderClass = objc_getClass("Lender");
unsigned int pOutCount;
objc_property_t *properties = class_copyPropertyList(LenderClass, &pOutCount);
for (int i = 0; i < pOutCount; i++) {
objc_property_t property = properties[i];
fprintf(stdout, "%s %s\n", property_getName(property), property_getAttributes(property));
}
free(properties);
printf("-----------\n");

unsigned int iOutCount;
Ivar *ivars = class_copyIvarList(LenderClass, &iOutCount);
for (int i=0; i<iOutCount; ++i) {
Ivar v = ivars[i];
fprintf(stdout, "%s %s\n", ivar_getName(v), ivar_getTypeEncoding(v));
}
free(ivars);
printf("-----------\n");
printf("property:%u, ivar:%u\n", pOutCount, iOutCount);

实现字典转模型

demo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
@import ObjectiveC.runtime;
@import ObjectiveC.message;

@implementation ModelMapping

- (instancetype)initWithDic:(NSDictionary *)dict {
self = [super init];
if (self) {
[self creatModelWithDic:dict];
}
return self;
}

- (void)creatModelWithDic:(NSDictionary *)dict {
unsigned int outCount = 0;
objc_property_t *properties = class_copyPropertyList(self.class, &outCount);
for (int i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
const char *propertyName = property_getName(property);
NSString *key = [NSString stringWithUTF8String:propertyName];

id value = nil;

if (![dict[key] isKindOfClass:[NSNull class]]) {
value = dict[key];
}else {
continue;
}

unsigned int count = 0;
objc_property_attribute_t *atts = property_copyAttributeList(property, &count);
objc_property_attribute_t att = atts[0];
NSString *type = [NSString stringWithUTF8String:att.value];
type = [type stringByReplacingOccurrencesOfString:@"“" withString:@""];
type = [type stringByReplacingOccurrencesOfString:@"@" withString:@""];

NSLog(@"type%@",type);

//数据为数组时
if ([value isKindOfClass:[NSArray class]]) {
Class class = NSClassFromString(key);
NSMutableArray *temArr = [[NSMutableArray alloc] init];
for (NSDictionary *tempDic in value) {
if (class) {
id model = [[class alloc] initWithDic:tempDic];
[temArr addObject:model];
}
}
value = temArr;
}

//数据为字典时
if ([value isKindOfClass:[NSDictionary class]] && ![type hasPrefix:@"NS"] ) {
Class class = NSClassFromString(key);
if (class) {
value = [[class alloc] initWithDic:value];
}
}

//赋值
SEL setterSel = [self findSetterWithKey:key];
if (setterSel != nil) {
((void (*)(id,SEL,id))objc_msgSend)(self,setterSel,value);
}
}
}

-(NSDictionary *)modelToDic {
NSMutableDictionary *dic = [[NSMutableDictionary alloc] init];

unsigned int outCount = 0;
objc_property_t *properties = class_copyPropertyList(self.class, &outCount);
for (int i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
const char *pName = property_getName(property);
NSString *key = [NSString stringWithUTF8String:pName];

SEL getter = [self findGetterWithKey:key];

if (getter) {
//有几种方式获得value
//id value = [self performSelector:getter];
//id value = [self valueForKey:key];
//NSMethodSignature *signature = [self methodSignatureForSelector:getter];
//NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
//[invocation setSelector:getter];
//[invocation invokeWithTarget:self];
//__unsafe_unretained NSObject *value = nil;
//[invocation getReturnValue:&value];

id value = ((id (*)(id,SEL))objc_msgSend)(self,getter);
if (value == nil) {
unsigned int outCount = 0;
objc_property_attribute_t *attributes = property_copyAttributeList(property, &outCount);
for (int i = 0; i <outCount; i++) {
objc_property_attribute_t att = attributes[i];
NSString *attValue = [NSString stringWithUTF8String:att.value];
if ([attValue isEqualToString:@"@\"NSDictionary\""]) {//字典
NSLog(@"key %@ is NSDictionary but nil",key);
value = [[NSDictionary alloc] init];
}

if ([attValue isEqualToString:@"@\"NSString\""]) {
NSLog(@"key %@ is NSString but nil",key);
value = @"";
}

if ([attValue isEqualToString:@"@\"NSInteger\""]) {
NSLog(@"key %@ is NSInteger but nil",key);
value = 0;
}

if ([attValue isEqualToString:@"@\"NSArray\""]) {
NSLog(@"key %@ is NSArray but nil",key);
value = @[];
}
}
}

[dic setValue:value forKey:key];
}
}

free(properties);
return dic;
}

#pragma mark Private Method
- (SEL)findSetterWithKey:(NSString *)key {
NSString *selStr = [NSString stringWithFormat:@"set%@:",key.capitalizedString];
SEL setterSEL = NSSelectorFromString(selStr);
if ([self respondsToSelector:setterSEL]) { //必须使用respondsToSelector:判断是否有相应方法 这里与Message Forwarding相关,防止异常,
return setterSEL;
}
return nil;
}

- (SEL)findGetterWithKey:(NSString *)key{
SEL getter = NSSelectorFromString(key);
if ([self respondsToSelector:getter]) {
return getter;
}
return nil;
}
@end

创建类&元类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/* Adding Classes */
/**
1. 使用 objc_allocateClassPair 创建出一个 class + metaclass
2. object_getClass(newClass) 得到新类的元类
3. 使用 class_addIvar,objc_registerClassPair 配置类的属性.
4. 注册 objc_registerClassPair
Instance methods and instance variables should be added to the class itself. Class methods should be added to the metaclass.

superclass:新类的父类
*/

OBJC_EXPORT Class _Nullable
objc_allocateClassPair(Class _Nullable superclass,
const char * _Nonnull name,
size_t extraBytes);

OBJC_EXPORT void
objc_registerClassPair(Class _Nonnull cls);

/* 销毁 cls & metaCls */
OBJC_EXPORT void
objc_disposeClassPair(Class _Nonnull cls)

/* 配置一个实例对象所属类 */
OBJC_EXPORT Class _Nullable
object_setClass(id _Nullable obj, Class _Nonnull cls);

Aspects 中生成子类的实例 demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const char *subclassName = [className stringByAppendingString:AspectsSubclassSuffix].UTF8String;
Class subclass = objc_getClass(subclassName);

if (subclass == nil) {
subclass = objc_allocateClassPair(baseClass, subclassName, 0);
if (subclass == nil) {
NSString *errrorDesc = [NSString stringWithFormat:@"objc_allocateClassPair failed to allocate class %s.", subclassName];
AspectError(AspectErrorFailedToAllocateClassPair, errrorDesc);
return nil;
}

aspect_swizzleForwardInvocation(subclass);
aspect_hookedGetClass(subclass, statedClass);
aspect_hookedGetClass(object_getClass(subclass), statedClass);
objc_registerClassPair(subclass);
}

object_setClass(self, subclass);

方法

method_exchangeImplementations 方法交换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@import ObjectiveC.runtime;

@implementation MethodExchange
- (void)method1 {
printf("%s\n", __func__);
}
- (void)method2{
printf("%s\n", __func__);
}

+ (void)test {
MethodExchange *normarlTest = [MethodExchange new];
[normarlTest method1];
printf("changed \n");
SEL s1 = NSSelectorFromString(@"method1");
SEL s2 = NSSelectorFromString(@"method2");
Method m1 = class_getInstanceMethod([MethodExchange class], s1);
Method m2 = class_getInstanceMethod([MethodExchange class], s2);
method_exchangeImplementations(m1, m2);
[normarlTest method1];
}
@end

通过方法交换 class_replaceMethod Hook 指定方法

具体步骤

  1. 确定要 hook 的方法 m1
  2. 给这个类添加个方法 _m1,内部调用 m1,_m1 中添加要hook 的信息
  3. swizzle method,交换 m1, _m1实现

这样当发消息的 m1 的时候会调用 _m1, _m1 内部调用 m1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@import ObjectiveC.runtime;
@implementation HookViewController

+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method m1 = class_getInstanceMethod([self class], @selector(viewWillAppear:));
Method m2 = class_getInstanceMethod([self class], @selector(_viewWillAppear:));

BOOL isSuccess = class_addMethod([self class], @selector(viewWillAppear:), method_getImplementation(m2), method_getTypeEncoding(m2));
if (!isSuccess) {
//添加失败:说明源方法已经有实现,直接将两个方法的实现交换即
method_exchangeImplementations(m1, m2);
}else {
// 添加成功:说明源方法m1现在的实现为交换方法m2的实现,现在将源方法m1的实现替换到交换方法m2中
class_replaceMethod([self class], @selector(_viewWillAppear:), method_getImplementation(m1), method_getTypeEncoding(m1));
}
});
}

-(void)viewWillAppear:(BOOL)animated {
printf("viewWillAppear\n");
}

- (void)_viewWillAppear:(BOOL)animated {
printf("hooked before: %s\n", __func__);
[self _viewWillAppear:YES];
printf("hooked after: %s\n", __func__);
}
@end