Objective-C只是在C语言层面上加了些关键字和语法。真正让Objective-C如此强大的是它的运行时。它很小但却很强大。它的核心是消息分发。

Message

  执行一个方法,有些语言、编译器会执行一些额外的优化和错误检查,因为调用的关系很直接也很明显。但是对于消息分发来说,就不一定了。在发消息前不必知道某个对象是否能处理消息,你把消息发给它,它可能会处理,也可能会交给其他的objec 处理。一个消息不用对应一个方法、一个对象也可能实现一个方法来处理多条消息。

在objcetive中,消息是通过objc_msgSend()这个runtime实现的。编译器把消息的分发转变成objc_msgSend执行。

id returnValue = [someobject messagename:parameter];

其中someObject是接收者(receiver),messagename叫做Selector,Selector和参数合起来叫做消息.

objc_msgSend(id self, sel cmd,...)

第一个参数代表接收者,第二个参数是Selector,后面的参数就是消息中得参数,位置顺序不变。所以根据上面的原型,我们可以把函数改写成这样:

id returnValue = objc_msgsend(someobject,@selector(messagename:),parameter);

  objc_msgsend函数会根据接受者和selector的类型调用适当的方法。会在接收者所属的类里面搜寻“方法列表”(list of method).如果能找到与selector名称相符合的方法,就跳转至实现代码。如果找不到的话,就会向上查找,等找到合适的方法后跳转。如果还是找不到得话就会执行“消息转发”的操作。

  按照这个思路,调用一个方法需要很多步骤。但是objc_msgsend会将结果缓存到快速映射表(fast map)里面,每个类都有一个这样的缓存,若是稍后还向该类发送相同的消息的话,执行起来就很快了。

对象某型

打开NSObject.h可以看见下面object_class的组成

打开runtime.h可以看见下面object_class的组成

isa指针:每个对象都是类的实例,isa指针指向这个实例所属的类,每个类也是一个对象,类也有isa指针。

super_class:父类

name:类的名字

info:类的一些信息

instance_size:实例的大小

objc_ivar_list:实例的参数列表

objc_method_list:实例的方法列表

objc_cache:方法的缓存

objc_protocol_list:协议方法列表

借用网上的一张图:图片来自这里

根据这张图片,可以发现有以下信息:

(1)类也是一个对象,这个对象是另外一个类的实例,这个类是meta(元类)

(2) 每个meta类也是一个对象,分别指向根meta类。

(3)根元类的isa指针指向自己,形成了一个闭环。

(4)在继承关系中,由于类方法的定义是保存在元类(metaclass)中,而方法调用的规则是,如果该类没有一个方法的实现,则向它的父类继续查找。所以,为了保证父类的类方法可以在子类中可以被调用,所以子类的元类会继承父类的元类,换而言之,类对象和元类对象有着同样的继承关系。

Method Swizzling

Method Swizzling 可以交换两个方法的实现。为什么会有这样的功能呢?首先看看扩展类的两种途径,第一种是子类化,重写父类的方法,然后调用父类的实现。但是使用子类的过程中,如果返回的时父类的类型的话怎么办?可以使用Category,添加一个扩展方法,这个方法如果没有和系统调用的方法重名的话一般情况下是没有问题的,但是如果重写了系统的方法的话,那么就永远不能调用这个方法了。所以Method Swizzling这个方法能解决这些问题,技能扩展类,又还能调用原来类的实现,通常情况下先建立一个与系统对应的扩展类,然后通过method_exchangeImplementations方法交换它们的实现。

首先定义一个NSString的扩展类

@interface NSString (Addition)
- (NSString *)test_myLowerString;
@end
@implementation NSString (Addition)

- (NSString *)test_myLowerString
{
NSString *lowercase = [self test_myLowerString];
NSLog(@"%@ =>%@",self,lowercase);
return lowercase;
} @end

然后交换它们的实现方法

    NSString *testString = @"TEST";
NSLog(@"%@,",[testString lowercaseString]); Method originMethod = class_getInstanceMethod([NSString class], @selector(lowercaseString));
Method swapMethod = class_getInstanceMethod([NSString class], @selector(test_myLowerString));
method_exchangeImplementations(originMethod, swapMethod);
NSLog(@"%@",[testString lowercaseString]);

打印的结果是:

所以我们改写了系统的lowercaseString方法,每当我们调用扩展的test_myLowerString方法时候,其实是调用系统的lowercaseString方法,这种做法一般情况用在调试系统,不过最好不建议改写系统的一些方法,可能会带来不可调试的后果,所以使用前需慎重。

动态方法处理和消息转发

上面谈了方法的交换,是对消息处理的一种,下面再谈另外一种方法的处理

参考链接

http://blog.devtang.com/blog/2013/10/15/objective-c-object-model/

http://limboy.me/ios/2013/08/03/dynamic-tips-and-tricks-with-objective-c.html

iOS-Runtime、对象模型、消息转发的更多相关文章

  1. iOS Runtime的消息转发机制

    前面我们已经讲解Runtime的基本概念和基本使用,如果大家对Runtime机制不是很了解,可以先看一下以前的博客,会对理解这篇博客有所帮助!!! Runtime基本概念:https://www.cn ...

  2. runtime之消息转发

    前言 在上一篇文章中我们初尝了runtime的黑魔法,可以在程序编译阶段就获取到成员变量的名字,特性以及动态的给对象增加属性等等,在接下来中我们进一步了解OC的消息发送机制.如果之前没接触过runti ...

  3. OC:浅析Runtime中消息转发机制

    一.介绍 OC是一门动态性语言,其实现的本质是利用runtime机制.在runtime中,对象调用方法,其实就是给对象发送一个消息,也即objc_msgSend().在这个消息发送的过程中,系统会进行 ...

  4. iOS开发·runtime原理与实践: 消息转发篇(Message Forwarding) (消息机制,方法未实现+API不兼容奔溃,模拟多继承)...

    本文Demo传送门: MessageForwardingDemo 摘要:编程,只了解原理不行,必须实战才能知道应用场景.本系列尝试阐述runtime相关理论的同时介绍一些实战场景,而本文则是本系列的消 ...

  5. iOS的消息转发机制详解

    iOS开发过程中,有一类的错误会经常遇到,就是找不到所调用的方法,当然这类问题比较好解决,给当前对象或其父类对象添加该方法即可,使得编译器在编译时能正确找到该方法:或者,还有另外的方法,由于Objec ...

  6. iOS消息转发

    消息转发是一种功能强大的技术,可以大大增加Objective-C的表现力.什么是消息转发?简而言之,它允许未知的消息被困住并作出反应.换句话说,无论何时发送未知消息,它​​都会以一个很好的包发送到您的 ...

  7. Runtime 运行时之一:消息转发

    解释一 上一篇文章咱们提到了Runtime的消息传递机制,主要围绕三个C语言API来展开进行的.这篇文章我将从另外三个方法来描述Runtime中另一个特性:消息转发机制. 一.消息转发机制 当向某个对 ...

  8. objc_msgSend消息传递学习笔记 – 消息转发

    该文是 objc_msgSend消息传递学习笔记 – 对象方法消息传递流程 的基础上继续探究源码,请先阅读上文. 消息转发机制(message forwarding) Objective-C 在调用对 ...

  9. iOS runtime探究(二): 从runtime開始深入理解OC消息转发机制

    你要知道的runtime都在这里 转载请注明出处 http://blog.csdn.net/u014205968/article/details/67639289 本文主要解说runtime相关知识, ...

随机推荐

  1. 调用start()与run()的区别

    1.调用start()方法: 通知“线程规划器”当前线程已经准备就绪,等待调用线程对象的run()方法.这个过程就是让系统安排一个时间来调用Thread中的run()方法,使线程得到运行,启动线程,具 ...

  2. HDU 5129 Yong Zheng's Death

    题目链接:HDU-5129 题目大意为给一堆字符串,问由任意两个字符串的前缀子串(注意断句)能组成多少种不同的字符串. 思路是先用总方案数减去重复的方案数. 考虑对于一个字符串S,如图,假设S1,S2 ...

  3. EOS.IO技术学习

    如今很火的项目EOS的学习,以下主要的内容是基于白皮书 参考: http://chainx.org/paper/index/index/id/20.html EOS.IO软件引入了一种新的块链架构,旨 ...

  4. Geoserver发布缓存切片(制定Gridsets)

    EPSG:4326 Level Pixel Size Scale Name Tiles   0 1: 2 x 1   1 1: 4 x 2   2 1: 8 x 4   3 1: 16 x 8   4 ...

  5. JVM字节码执行引擎和动态绑定原理

    1.执行引擎 所有Java虚拟机的执行引擎都是一致的: 输入的是字节码文件,处理过程就是解析过程,最后输出执行结果. 在整个过程不同的数据在不同的结构中进行处理. 2.栈帧 jvm进行方法调用和方法执 ...

  6. [译]怎样用HTML5 Canvas制作一个简单的游戏

    这是我翻译自LostDecadeGames主页的一篇文章,原文地址:How To Make A Simple HTML5 Canvas Game. 下面是正文: 自从我制作了一些HTML5游戏(例如C ...

  7. hdu 3667(最小费用最大流+拆边)

    Transportation Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)To ...

  8. Zabbix历史数据库迁移 及分区

    https://blog.csdn.net/hkyw000/article/details/78971201?utm_source=blogxgwz6

  9. 201. Non Absorbing DFA

    题意好难看懂的说... 有限状态自动机DFA是这么一个有序组<Σ, U, s, T, phi>:Σ代表输入字符集,表示此自动机的工作范围:U代表所有的状态集合:s是初始状态:T是最终状态: ...

  10. WebDriver自动化测试工具(3)---PhantomJS的使用

    PhantomJS是一个基于webkit的javascript API.它使用QtWebKit作为它核心浏览器的功能,使用webkit来编译解释执行JavaScript代码.任何你可以在基于webki ...