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. Bypass disable_functions 食用方法

    Bypass disable_functions 食用方法 目录 Bypass disable_functions 食用方法 1 上传Payload 2 直接使用sh反弹shell 3 上传 Payl ...

  2. Spring Boot 整合 Logback 日志

    Spring Boot 支持 Java Util Logging,Log4J,Log4J2 和 Logback 等日志框架,默认采用 Logback 日志. 在实际 Spring Boot 项目中使用 ...

  3. windows pwn(一)

    前言 前几天因为看CS shellcode装了一个win10虚拟机,然后正好因为逆向课老师要装一系列工具.于是就想起来之前一直想看的windows pwn,就顺便装了一下相关工具并且入门了一下. 工具 ...

  4. Java 反射概念的引入

    反射是什么 学Java的人都知道类概念,反射技术就是一种控制类的技术,JAVA程序在运行时,通过反射这个技术,能动态的获取到类实例的信息.创建实体类.操作实体类. 反射的功能列表: 获取任意类的名称. ...

  5. Java的注释和标识符

    java 的注释规则 1. 单行注释 语法:// 描述://用于当行注释,与JavaScript一样 //这是一个代码注释 2. javadoc注释 语法: /** *注释内容 / 描述:多行注释中可 ...

  6. PYTHON编写程序练习-打印99乘法表

    使用for循环嵌套的知识点编写 for i in range(1,10):   #第一层循环,循环乘数 for j in range(1,i+1):   #第二层循环,循环被乘数 print(f&qu ...

  7. mysql 数据库的一些参数,常用模版和调优方式

    innodb_buffer_pool_size :这个参数是Mysql数据库最重要的参数之一,表示InnoDB类型的 表 和索引的最大缓存 .它不仅仅缓存 索引数据 ,还会缓存 表的数据 .这个值越大 ...

  8. C#获取各种当前日期时间

    我们可以通过使用DataTime这个类来获取当前的时间.通过调用类中的各种方法我们可以获取不同的时间:如:日期(2008-09-04).时间(12:12:12).日期+时间(2008-09-04 12 ...

  9. Day 23 23.2:逆向前期准备

    逆向前期准备 环境安装 pip install PyExecJS(自己pip安装,安装成功后,最好重启下cmd终端和pycharm,或者重启电脑) 安装node.js开发环境:安装好了之后,记得重启电 ...

  10. shiyansi

    #include <stdio.h> #include <stdlib.h> #define N 1000 int fun(int n,int m,int bb[N]) { i ...