iOS笔记 - runtime 02:objc_msgSend执行流程
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 }
日志信息