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 }

日志信息

iOS笔记 - runtime 02:objc_msgSend执行流程的更多相关文章

  1. Tomcat笔记:Tomcat的执行流程解析

    Bootstrap的启动 Bootstrap的main方法先new了一个自己的对象(Bootstrap),然后用该对象主要执行了四个方法: init(); setAwait(true); load(a ...

  2. iOS回顾笔记( 02 ) -- 由九宫格布局引发的一系列“惨案”

    html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,bi ...

  3. TIJ读书笔记02-控制执行流程

      TIJ读书笔记02-控制执行流程 TIJ读书笔记02-控制执行流程 if-else 迭代 无条件分支 switch语句 所有条件语句都是以条件表达式的真假来决定执行路径,也就是通过布尔测试结果来决 ...

  4. MySQL笔记(5)-- SQL执行流程,MySQL体系结构

    MySQL的体系结构,可以清楚地看到 SQL 语句在 MySQL 的各个功能模块中的执行过程:Server层包括连接层.查询缓存.分析器.优化器.执行器等,涵盖MySQL的大多数核心服务功能,以及所有 ...

  5. 040 01 Android 零基础入门 01 Java基础语法 05 Java流程控制之循环结构 02 while循环的执行流程

    040 01 Android 零基础入门 01 Java基础语法 05 Java流程控制之循环结构 02 while循环的执行流程 本文知识点:while循环的执行流程 三种循环结构中的第一种--wh ...

  6. [Java编程思想-学习笔记]第4章 控制执行流程

    4.1  return 关键字return有两方面的用途:一方面指定一个方法结束时返回一个值:一方面强行在return位置结束整个方法,如下所示: char test(int score) { if ...

  7. SpringMVC学习笔记一:基本概念,执行流程与开发步骤

    转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6831328.html  一:基本概念 DispatcherServlet:前端控制器,负责把请求分发处理. H ...

  8. (一)熟悉执行流程——基于ThinkPHP3.2的内容管理框架OneThink学习

    ThinkPHP作为国内具有代表性的PHP框架,经过多年的发展,受到越来越多公司与开发者的青睐.我也在忙里偷闲中抽出部分时间,来学习这个优秀的框架.在开始学习这个框架时,最好通过实例来学习,更容易结合 ...

  9. iOS 开发-- Runtime 1小时入门教程

    1小时让你知道什么是Objective-C Runtime,并对它有一定的基本了解,可以在开发过程中运用自如. 三.Objective-C Runtime到底是什么东西? 简而言之,Objective ...

  10. iOS开发-Runtime详解

    iOS开发-Runtime详解 简介 Runtime 又叫运行时,是一套底层的 C 语言 API,其为 iOS 内部的核心之一,我们平时编写的 OC 代码,底层都是基于它来实现的.比如: [recei ...

随机推荐

  1. 代码随想录算法训练营day14 | leetcode 层序遍历 226.翻转二叉树 101.对称二叉树 2

    层序遍历 /** * 二叉树的层序遍历 */ class QueueTraverse { /** * 存放一层一层的数据 */ public List<List<Integer>&g ...

  2. WPF HandyOrg DataGrid 表格内容和标题居中显示

    表格内容居中 对于文本显示列DataGridTextColumn需要设定文本内容水平居中或者水平居右,而不是HandyControl中设定的样式默认显示为居左时,需要继承DataGridCellSty ...

  3. ASP.NET Core - 配置系统之配置读取

    一个应用要运行起来,往往需要读取很多的预设好的配置信息,根据约定好的信息或方式执行一定的行为. 配置的本质就是软件运行的参数,在一个软件实现中需要的参数非常多,如果我们以 Hard Code(硬编码) ...

  4. Java打印裴波那契数列

    //裴波那契数列的定义:他的第一项和第二项均为1,以后各项都为前两项的和.如: 1,1,2,3,5,8,13,21,34,55,89,144,233,~~~~ 关键代码如下: package fuxi ...

  5. getUserInfo和getUserProfile被废弃

    之前得知获取用户头像和昵称的两个接口getUserInfo和getUserProfile被废弃了,于是我就想深入探究一下. 一直抱有一个疑问,为啥有getUserInfo和getUserProfile ...

  6. #加IPV6路由

    #加IPV6路由route -A inet6 add 2409:802f:6005:4204::31:0/123 gw 2409:805b:6005:4218::41f:1#路由写入网卡配置文件vi ...

  7. @NotNull,@NotBlank,@NotEmpty注解的区别

    开发中常看见@NotNull,@NotBlank,@NotEmpty三个注解,但却没有深入了解过,下面介绍一下他们的应用场景和区别 @NotNull:主要用在基本数据类型上(Int,Integer,D ...

  8. ts(keyof,typeof)

    ts(keyof,typeof) keyof 获取某类型的键,返回联合类型 interface Person { hair: string; eyesColor: string; } let P1 = ...

  9. 关于java业务限流组件的应用推广

    可参考的链接如下: 限流算法对比.网关限流实践总结(https://segmentfault.com/a/1190000020745218) 高并发下常见的限流算法(https://www.jians ...

  10. vue项目打包后的文件如何在本地访问

    你是不是一直存在个困惑?vue项目build出来的dist文件夹下index.html直接点开始控制台一顿报错.今天咱就给他治服. 解决方案就是本地启动一个node服务.详细步骤如下: 创建项目 np ...