objc_msgSend 执行流程

1 – 第一步:消息发送

2 – 第二步:动态解析

  

代码示例:resolveInstanceMethod | resolveClassMethod

存在问题:68 行动态添加类方法,91 行调用崩溃,无解中…….

 1 #import <Foundation/Foundation.h>
 2 #import <malloc/malloc.h>
 3 #import <objc/runtime.h>
 4 @interface Person : NSObject
 5 
 6 @end
 7 
 8 @implementation Person
 9 //-------------------------------------实例方法 resolveInstanceMethod
10 -(void)makeMethods{
11     NSLog(@"----%s----",__func__);
12 }
13 
14 // C 函数
15 void c_method(id self, SEL _cmd){
16     NSLog(@"c_method----%@",NSStringFromSelector(_cmd));
17 }
18 
19 // 搞一个 method_t
20 struct method_t{
21     SEL sel;
22     char *types;
23     IMP imp;
24 };
25 
26 // 动态解析:实例方法
27 +(BOOL)resolveInstanceMethod:(SEL)sel{
28     
29     // 调用不存在的实例方法 testOne
30     if (sel == @selector(testOne)) {
31         
32         // 对象方法存放在类对象中,所以这里直接使用 self
33         Method method01 = class_getInstanceMethod(self, @selector(makeMethods));
34         class_addMethod(self, sel, method_getImplementation(method01), method_getTypeEncoding(method01));
35         
36         
37         // Method 结构体,其实同 method_t。我们在上面搞一个 method_t 可简单验证:同样可以执行
38         // struct method_t *method = (struct method_t *)class_getInstanceMethod(self,@selector(makeMethods));
39         // class_addMethod(self, sel, method->imp,  method->types);
40 
41 
42         // 同样可以添加 C 函数
43         // class_addMethod(self,sel,c_method,"v16@0:8");
44         
45         
46         // 默认 YES,表示已动态解析
47         return YES;
48     }
49     
50     return [super resolveInstanceMethod:sel];
51 }
52 
53 //-------------------------------------类方法 resolveClassMethod
54 + (void)doSomethindgs{
55     NSLog(@"----%s----",__func__);
56 }
57 
58 // c 函数
59 void c_other(id self, SEL _cmd){
60     NSLog(@"c_other - %@ - %@", self, NSStringFromSelector(_cmd));
61 }
62 // 动态解析:类方法
63 +(BOOL)resolveClassMethod:(SEL)sel{
64     // 调用不存在的类方法 testTwo
65     if (sel == @selector(testTwo)) {
66         
67         // 类方法要添加在元类对象中 objc_getClass(self)
68         // Method methodNew = class_getClassMethod(object_getClass(self), @selector(doSomethindgs));
69         // class_addMethod(object_getClass(self), sel, method_getImplementation(methodNew), method_getTypeEncoding(methodNew));
70         
71         class_addMethod(object_getClass(self), sel, (IMP)c_other, "v16@0:8");
72         return YES;
73     }
74     
75     return [super resolveClassMethod:sel];
76 }
77 
78 @end
79 
80 //----------- main ------------
81 int main(int argc, const char * argv[]) {
82     
83     @autoreleasepool {
84         Person *ins_Person = [Person new];
85         
86         // 实例方法
87         [ins_Person testOne]; // 调用成功
88         [ins_Person performSelector:@selector(testOne)]; // 调用成功
89         
90         // 类方法
91         [Person testTwo];
92     }
93     
94     return 0;
95     
96 }

3 – 第三步:消息转发

代码实例:Person 将消息转发给 Animal

  1 #import <Foundation/Foundation.h>
  2 #import <malloc/malloc.h>
  3 #import <objc/runtime.h>
  4 //-------- Animal --------
  5 @interface Animal : NSObject
  6 
  7 // 实例方法
  8 // 带参有返回值,方面加深理解
  9 -(int)doSomethings:(int)no eatThings:(NSString*)food;
 10 
 11 // 类方法
 12 // 注:好多博主都说 iOS 没有类方法的消息转发,这是不严谨的
 13 //    我们通过逆向编程、国外大神的伪代码,很容易推演来类方法的消息转发机制,系统不会智能提示
 14 +(void)testMade;
 15 @end
 16 
 17 @implementation Animal
 18 -(int)doSomethings:(int)no eatThings:(NSString*)food{
 19     NSLog(@" no 是 %d;   food 是 %@",no,food);
 20     return no * 2;
 21 }
 22 
 23 +(void)testMade{
 24     NSLog(@"Animal 的类方法");
 25 }
 26 @end
 27 
 28 //-------- Person --------
 29 @interface Person : NSObject
 30 
 31 @end
 32 
 33 @implementation Person
 34 
 35 // 消息转发:自己做不了的事情甩锅给别人处理
 36 
 37 // ----------------实例方法
 38 // 状况一:有返回值:直接在返回的 Animal 中执行该方法
 39 //- (id)forwardingTargetForSelector:(SEL)aSelector{
 40 //
 41 //    NSLog(@"没有动态解析,进入 Person 的消息转发 %@",NSStringFromSelector(aSelector));
 42 //
 43 //    if (aSelector == @selector(doSomethings:eatThings:)) {
 44 //        // 返回 Animal对象,意味着交给 Animal 处理 doSomethings:eatThings:方法
 45 //        return [[Animal alloc] init];// 会触发 doSomethings:eatThings:方法
 46 //
 47 //        // 根据逆向编程猜想,返回值其实就是干了这么一件事
 48 //        // objc_msgSend([[Animal alloc] init],aSelector)
 49 //    }
 50 //    return [super forwardingTargetForSelector:aSelector];
 51 //}
 52 
 53 // 状况二:没有返回值
 54 - (id)forwardingTargetForSelector:(SEL)aSelector{
 55     
 56     NSLog(@"没有动态解析,进入 Person 的消息转发 %@",NSStringFromSelector(aSelector));
 57     
 58     if (aSelector == @selector(doSomethings:eatThings:)) {
 59         
 60         return nil;
 61     }
 62     return [super forwardingTargetForSelector:aSelector];
 63 }
 64 
 65 // 消息转发若为空,则进入方法签名:返回方法返回值类型、参数类型
 66 - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
 67     if (aSelector == @selector(doSomethings:eatThings:)) {
 68         
 69         // 方式一:使用 typeEncoding 编码
 70         // 这里 typeEncoding 编码无需那么规范,只写类型即可
 71         return [NSMethodSignature signatureWithObjCTypes:"i@:i@"];
 72         
 73         // 不建议
 74         // 方式二:道理如此,但是你这里重写了该方法又在内部调用自身方法,陷入死循环,crash
 75         // return [NSMethodSignature methodSignatureForSelector:aSelector];
 76         // 改正:使用调用者进行方法签名
 77         // return [[[Animal alloc] init] methodSignatureForSelector:aSelector];
 78     }
 79     return [super methodSignatureForSelector:aSelector];
 80 }
 81 
 82 // NSInvocation 封装了方法相关信息(方法调用者、方法返回值、参数等等)
 83 - (void)forwardInvocation:(NSInvocation *)anInvocation{
 84     
 85     // anInvocation.target    方法调用者
 86     // anInvocation.selector  方法名
 87     // .....
 88     
 89     // 方式一
 90     // 指定调用者
 91     anInvocation.target = [[Animal alloc] init];
 92     // 开启调用
 93     [anInvocation invoke];
 94     
 95     // 方式二
 96     [anInvocation invokeWithTarget:[[Animal alloc] init]];
 97     
 98     // 获取参数
 99     NSString *food;
100     [anInvocation getArgument:&food atIndex:3];
101     NSLog(@"取出参数:%@",food);
102     
103 }
104 
105 
106 // ----------------类方法(流程同实例方法)
107 // 状况一:有返回值
108 //+ (id)forwardingTargetForSelector:(SEL)aSelector{
109 //
110 //    if (aSelector == @selector(testMade)) {
111 //
112 //        // 注:这里方法接收者不是类对象,而是元类对象
113 //        // return [[Animal alloc] init]; // 是错误的
114 //
115 //        return [Animal class];
116 //    }
117 //    return [super forwardingTargetForSelector:aSelector];
118 //}
119 
120 // 状况二:没有返回值
121 + (id)forwardingTargetForSelector:(SEL)aSelector{
122 
123     if (aSelector == @selector(testMade)) {
124 
125         return nil;
126 
127     }
128     return [super forwardingTargetForSelector:aSelector];
129 }
130 
131 // 方法签名
132 + (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
133     if (aSelector == @selector(testMade)) {
134         return [NSMethodSignature signatureWithObjCTypes:"v@:"];
135     }
136     return [super methodSignatureForSelector:aSelector];
137 }
138 
139 + (void)forwardInvocation:(NSInvocation *)anInvocation{
140     [anInvocation invokeWithTarget:[Animal class]];
141 }
142 
143 @end
144 
145 //----------- main ------------
146 int main(int argc, const char * argv[]) {
147     
148     @autoreleasepool {
149         
150         Person *ins_Person = [Person new];
151         // 实例方法
152         [ins_Person doSomethings:5 eatThings:@"chicken"];
153         
154         // 类方法
155         [Person testMade];
156         
157     }
158     
159     return 0;
160     
161 }

日志信息

 

 

版权声明:本文为CodingLife ✎ 原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/self-epoch/p/16464438.html