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. ICTPOS3.0 词性标注集

    Ag 形语素 形容词性语素.形容词代码为a,语素代码g前面置以A. a 形容词 取英语形容词adjective的第1个字母. ad 副形词 直接作状语的形容词.形容词代码a和副词代码d并在一起. an ...

  2. css3属性书写的时候带的一些前缀的意思

    使用css3属性时,大部分都要带这些识别前缀,早期点的浏览器才能识别.现在最新版的浏览器基本都支持css3 基本都不用写前缀 ,写前缀是为了向前兼容老版本的浏览器而已. -ms-transform: ...

  3. MySQL数据库分表分区(一)(转)

    面对当今大数据存储,设想当mysql中一个表的总记录超过1000W,会出现性能的大幅度下降吗? 答案是肯定的,一个表的总记录超过1000W,在操作系统层面检索也是效率非常低的   解决方案: 目前针对 ...

  4. 关于ueditor在Java中文件上传问题,404问题

    问题困扰了两天,部署要求导入到webcontent下,我导入到了整个项目目录下,自己粗心犯错,导致页面访问不到404. 解决了上面的问题,试着进行文件上传,却一直找不到图片: 调出浏览器控制台: 刚开 ...

  5. 封装ajax方法

    function ajaxRequest(type, url, data, callback, loading, cache) { var ajaxConfig = { url: '', data: ...

  6. numpy及scipy的使用

    numpy的使用 把list A转换为numpy 矩阵 np.array(A) np.array(A, 'int32') numpy加载txt文件里面的矩阵 matrix = np.loadtxt(t ...

  7. 关于xargs cp中,如何确定拷贝的源和目的

    来源: http://bbs.chinaunix.net/thread-1022095-1-1.html Seker: find . -name "*" |xargs cp ??? ...

  8. cocos2d-js中怎么删除一个精灵

    添加元素时,有Name属性 var child = parent.addChild(label, 1, "元素的名字"); 或者给child设置tag child.setTag(& ...

  9. [putty] ubuntu 通过配置文件设置字体

    创建了一个session之后,就能在 ~/.putty/sessions/ 文件夹下看到session的配置文件了 $ vim ~/.putty/sessions/session-name 搜索Fon ...

  10. 【应用】wamp3.x.x设置,让外网能够访问本服务器

    开始教程前,先来看一看本机的运行环境. WAMP 32位版 3.0.6 WIN7 x86 企业版 其他VC运行库全部安装,NET装了3.5以及4.0,还有一些运行库这里省略 ——我是华丽的分割线——— ...