http://www.jianshu.com/p/95c8cb186673

在OC中,我们对方法的调用都会被转换成内部的消息发送执行对objc_msgSend方法的调用,掌握好消息发送,可以让我们在编程中更方便灵活。

首先来看下方法定义:

/**  定义:'为某个类对象发送消息,并且返回一个值'
参数1: 消息接收的对象实例
参数2: 要执行的方法
...: 一系列其他参数 */
id objc_msgSend(id self, SEL op, ...)

这里有官方文档的解释

我们创建一个MessageSendTest文件,在.m文件中定义四个方法用于测试:

// 无参数 无返回值
- (void)noArgumentsAndNoReturnValue
{
NSLog(@"方法名:%s", __FUNCTION__);
} // 带一个参数 无返回值
- (void)hasArguments:(NSString *)arg
{
NSLog(@"方法名:%s, 参数:%@", __FUNCTION__, arg);
} // 无参数 有返回值
- (NSString *)noArgumentsButReturnValue
{
NSLog(@"方法名:%s, 返回值:%@", __FUNCTION__, @"不带参数,但是带有返回值");
return @"不带参数,但是带有返回值";
} // 带两个参数 有返回值
- (int)hasArguments:(NSString *)arg andReturnValue:(int)arg1
{
NSLog(@"方法名:%s, 参数:%@, 返回值:%d", __FUNCTION__, arg, arg1);
return arg1;
}

然后我们再定义一个测试方法:

+ (void)test
{
}

在测试方法里边,我们:

  • 调用无参无返回值方法

      // 1、创建对象
    // 给'MessageSendTest'类发送消息,创建对象,这句话等同于 MessageSendTest *test = [MessageSendTest alloc];
    MessageSendTest *test = ((MessageSendTest * (*)(id,SEL)) objc_msgSend)((id)[MessageSendTest class], @selector(alloc)); // 2、初始化对象
    // 给'test'对象发送消息进行初始化,这句话等同于 [test init];
    test = ((MessageSendTest *(*)(id,SEL))objc_msgSend)((id)test, @selector(init));
    NSLog(@"test:%@", test); // 3、调用无参无返回值方法
    ((void(*)(id,SEL))objc_msgSend)((id)test, @selector(noArgumentsAndNoReturnValue));

    从上边三行代码我们不难看出,每次给对象发送消息,objc_msgSend都至少要带有(id, SEL)两个参数,其中'1'和'2'里边返回值类型为MessageSendTest *类型,'3'里边返回值类型为void类型。这样我们就创建了一个MessageSendTest的对象,并且调用了noArgumentsAndNoReturnValue方法

    打印结果:(由打印结果可见,我们已经达到了预期目的)

      2016-07-04 15:59:46.543 ZFRuntime[1378:216191] test:<MessageSendTest: 0x7fafe0c733d0>
    2016-07-04 15:59:46.543 ZFRuntime[1378:216191] 方法名:-[MessageSendTest noArgumentsAndNoReturnValue]
  • 调用带一个参数但无返回值的方法

      // 4、调用带一个参数但无返回值的方法
    ((void(*)(id,SEL,NSString *))objc_msgSend)((id)test, @selector(hasArguments:), @"带一参数但是没有返回值");

    相比于上边的方法,这里我们在 = 左边多了个NSString@"带一参数但是没有返回值",这就是我们要给这个方法传递的参数

    打印结果:(由打印结果可见,我们已经达到了预期目的)

      2016-07-04 16:10:24.800 ZFRuntime[1415:225374] 方法名:-[MessageSendTest hasArguments:], 参数:带一参数但是没有返回值
  • 调用带返回值,但是不带参数的方法

      NSString *returnStr = ((NSString * (*) (id, SEL)objc_msgSend))((id)test, @selector(noArgumentsButReturnValue));
    NSLog(@"5. 返回值为:%@", reuturnStr);

    打印结果:(由打印结果可见,我们已经达到了预期目的)

      2016-07-04 16:13:53.764 ZFRuntime[1434:229571] 方法名:-[MessageSendTest noArgumentsButReturnValue], 返回值:不带参数,但是带有返回值
    2016-07-04 16:13:53.764 ZFRuntime[1434:229571] 5. 返回值为:不带参数,但是带有返回值
  • 调用带参数带返回值的方法

      int returnInt = ((int *(id, SEL, NSString *, int))objc_msgSend)((id)test, @selector(hasArguments:andReturnValue:), @"参数1", 1024);
    NSLog(@"6. return value is %d", reuturnInt);

    打印结果:(由打印结果可见,我们已经达到了预期目的)

      2016-07-04 16:18:38.679 ZFRuntime[1455:234403] 方法名:-[MessageSendTest hasArguments:andReturnValue:], 参数:参数1, 返回值:1024
    2016-07-04 16:18:38.679 ZFRuntime[1455:234403] 6. return value is 1024
  • 我们还可以给类动态地添加方法

      class_addMethod([test class], NSSelectorFromString(@"cStyleFunc"), (IMP)cStyleFunc, "i@:r^vr^v");
    int returnValue = ((int *(id, SEL, const void *, const void *))objc_msgSend)((id)test, NSSelectorFromString(@"cStyleFunc"), "参数1", "参数2");
    NSLog(@"7. 返回值:%d", returnValue); // 然后我们实现方法 `cStyleFunc`
    int cStyleFunc(id receiver, SEL sel, const void *arg1, const void *arg2)
    {
    NSLog(@"方法名:%s, 参数1:%@, 参数2:%@", __FUNCTION__, [NSString stringWithUTF8String:arg1], [NSString stringWithUTF8String:arg1]);
    return 1;
    }

    先来看下打印结果:

      2016-07-04 16:24:36.670 ZFRuntime[1477:241676] 方法名:cStyleFunc, 参数1:参数1, 参数2:参数1
    2016-07-04 16:24:36.670 ZFRuntime[1477:241676] 7. 返回值:1

    从打印来看,结果是正常的,那么我们再来分析下class_addMethod这个方法,对于这个API苹果是这样定义的

      /**
    * 定义:给定名称和实现,从而为类增加新的方法
    * cls:要增加方法的那个类
    * name:方法选择器
    * imp:具体的实现函数
    * types:一串描述方法参数的字符串
    */
    BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)

    对于上边这个方法,参数1、2和3我们都没有疑惑,可是第4个参数,让人看着云里雾里,完全不知道是什么东西,其实查看官方文档不难发现

    对于 "i@:r^vr^v"

      第1个字符:表示函数(方法)返回值类型,这里返回值类型是 `int` ,故为 `i`
    第2、3个字符:苹果解释是由于函数(方法)至少带有两个参数(self和_cmd)还记得之前的 (id,SEL) 么,所以第2、3个字符必须是 ‘@:’,其实我们当做固定写法就好了
    第4个字符之后的是什么呢?

    不要着急,我们在刚才的输出语句 NSLog(@"7. 返回值:%d", returnValue); 之后加一句打印:

      NSLog(@"%s", @encode(const void *));

    这个时候控制台输出了一句话:

      2016-07-04 16:44:32.300 ZFRuntime[1522:262609] r^v

    看到这里我们就明白了,r^v其实代表的是cStyleFunc函数第3、4个参数类型,当然对于不同情况,这里的r^v是不同的,具体其他情况可以参考苹果给出的解释

备注

在使用objc_msgSend方法编译时可能出现报错的情况,对应的解决办法如下:

本篇笔记部分参考自一下:

runtime - 消息发送(objc_msgSend)的更多相关文章

  1. Runtime - 消息发送原理

    Runtime - 消息发送原理. Objective-C运行时的核心就在于消息分派器objc_msgSend,消息分派器把选择器映射为函数指针,并调用被引用的函数. 要想理解objc_msgSend ...

  2. iOS 消息发送与转发详解

    Objective-C 是一门动态语言,它将很多静态语言在编译和链接时期做的事情,放到了运行时来处理.之所以能具备这种特性,离不开 Runtime 这个库.Runtime 很好的解决了如何在运行时期找 ...

  3. runtime——消息机制

    本文授权转载,作者:Sindri的小巢(简书) 从异常说起 我们都知道,在iOS中存在这么一个通用类类型id,它可以用来表示任何对象的类型 —— 这意味着我们使用id类型的对象调用任何一个方法,编译器 ...

  4. Objective-C中的消息发送总结

    关于OC中的消息发送的实现,在去年也看过一次,当时有点不太理解,但是今年再看却很容易理解. 我想这跟知识体系的构建有关,如果你不认识有砖.水泥等这些建筑的基本组成部分,那么我们应该很难理解建筑是怎么建 ...

  5. ios学习路线—Objective-C(Runtime消息机制)

    RunTime简称运行时.就是系统在运行的时候的一些机制,其中最主要的是消息机制.对于C语言,函数的调用在编译的时候会决定调用哪个函数( C语言的函数调用请看这里 ).编译完成之后直接顺序执行,无任何 ...

  6. runtime - 消息机制

    Xcode中使用runtime代码时,建议先做下配置: 使用runtime代码时会有适当的提醒. OC方法调用的本质是消息转发,消息机制的本质 创建一个Person类,添加方法 - (void)eat ...

  7. runtime消息转发机制

    Objective-C 扩展了 C 语言,并加入了面向对象特性和 Smalltalk 式的消息传递机制.而这个扩展的核心是一个用 C 和 编译语言 写的 Runtime 库.它是 Objective- ...

  8. ActiveMQ点对点的消息发送案例

    公司最近会用MQ对某些业务进行处理,所以,这次我下载了apache-activemq-5.12.0-bin把玩下. 基于练习方便需要,使用Windows的版本. 参考的优秀文章: activemq的几 ...

  9. 【ActiveMQ】ActiveMQ在Windows的安装,以及点对点的消息发送案例

    公司最近会用MQ对某些业务进行处理,所以,这次我下载了apache-activemq-5.12.0-bin把玩下. 基于练习方便需要,使用Windows的版本. 参考的优秀文章: activemq的几 ...

随机推荐

  1. Zhe Second Language——

    You got things to do. Place to go. People to see. Futures to make. 我们还有事要做,有地儿要去,有人要见,有梦要实现. Remembe ...

  2. 用Powershell强制同步Windows主机与Internet time server的时间

    第一步,判断Windows Time服务是否正在运行,如果没有,则开启它. 第二步,强制同步,不知为何,往往第一次会失败,那么就多运行几次好了. Get-Service w32time | Where ...

  3. golang gin框架设置静态目录

    router := gin.Default() 第一个参数是api 第二个静态问价的文件夹相对目录 router.StaticFS("/data", http.Dir(" ...

  4. C++分治策略实现线性时间选择

    问题描述: 给定线性序集中n个元素和一个整数k,1≤k≤n,要求找出这n个元素中第k小的元素,即如果将这n个元素依其线性序排列时,排在第k个的元素即为要找到元素. 细节须知:(与之前的随笔相比) (1 ...

  5. CSAPP第二章show_bytes函数的探究

    CSAPP第二章中给出了一个帮助我们观察数据的位模式的函数--show_bytes函数,具体实现如下: #include<stdio.h> typedef unsigned char *b ...

  6. SAP替代,出口U904在RGGBS000中未生成

    报错.提示出口U904在RGGBS000中未生成. 一般情况下需要到 程序RGGBS000 中,在form:get_exit_titles 中增加下列代码. exits-name = 'U904. e ...

  7. javascript中五种迭代方法实例

    温习一下js中的迭代方法. <script type="text/javascript"> var arr = [1, 2, 3, 4, 5, 4, 3, 2, 1]; ...

  8. 剑指Offer 总结

    给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null. public class Solution { public ListNode EntryNodeOfLoop(ListNo ...

  9. Django---Django中使用COOKIE和SESSION

    Django---Django中使用COOKIE和SESSION 一丶Cookie cookie的由来 # HTTP协议是无状态的. # 无状态的意思是每次请求都是独立的,它的执行情况和结果与前面的请 ...

  10. python基础05day--函数

    一 函数知识体系 什么是函数?为什么要用函数?函数的分类:内置函数与自定义函数如何自定义函数 语法 定义有参数函数,及有参函数的应用场景 定义无参数函数,及无参函数的应用场景 定义空函数,及空函数的应 ...