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. 一个更适合Java初学者的轻量级开发工具:BlueJ

    Java是世界上最流行的编程语言之一,它被广泛用于从Web开发到移动应用的各种应用程序.大部分Java工程师主要是用IDEA.Eclipse为主,这两个开发工具由于有强大的能力,所以复杂度上就更高一些 ...

  2. Git介绍下载安装以及基本使用

    目录 一.git介绍 二.下载安装git软件 三.基本使用 四.制作忽略文件 五.Git.Gitee.GitHub.Gitlab.bitbucket的区别 六.基础代码操作分类 一.git介绍 git ...

  3. k8s-分布式系统架构master-worker

    K8S系列一:概念入门 - 知乎 (zhihu.com) 大白话先了解k8s. k8s是为容器服务而生的一个可移植容器的编排管理工具 概述 Master-Workers 架构(粗译为主从架构)是分布式 ...

  4. RESTful风格与Spring注解

    RESTfulL是一种网络应用程序的设计风格和开发方式,即接口请求方式和路径的一种风格. 普通风格: localhost:8080/add?a=1&b=2 RestFul风格: localho ...

  5. div溢出横向滚动

    需求:div在一行内需要溢出滚动 方案: 1:父类元素需要设置 overflow-x: auto;  //横向方向溢出元素 white-space: nowrap; //溢出的元素不换行 2:子元素需 ...

  6. gunicorn的功能及使用方法

    一.gunicorn的简介Gunicorn是基于unix系统,被广泛应用的高性能的Python WSGI HTTP Server.用来解析HTTP请求的网关服务.它通常是在进行反向代理(如nginx) ...

  7. C++ MFC学习 (三)

    视窗口覆盖在框架窗口之上 如果有了 OnDraw 不要再用OnPaint  OnPaint会覆盖掉OnDraw所画内容 1 // MFC_Demo1View.cpp : CMFC_Demo1View ...

  8. Git 知识

    1.git merge .git cherry-pick.git rebase 可以看出merge结果能够体现出时间线,但是rebase会打乱时间线. 而rebase看起来简洁,但是merge看起来不 ...

  9. 数据库基础day1

    数据库基础 MySQL概述 SQL 函数 概念 函数 是指一段可以直接被另一段程序调用的程序或代码. 3.1字符串函数 函数 功能 CONCAT(S1,S2,...Sn) 字符串拼接,将S1,S2,. ...

  10. 关于paddleocr2.6 布局分析的踩坑总结(一)

    8月24日paddleocr发布了2.6.0,之前使用过2.5版本的布局分析,整体比较好用.近期就尝试了一下paddleocr的新版本,记录一下尝鲜经历.2.6版本的公告中指出,布局分析模型缩小了95 ...