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. python爬虫模块之URL管理器模块

    URL管理器模块 一般是用来维护爬取的url和未爬取的url已经新添加的url的,如果队列中已经存在了当前爬取的url了就不需要再重复爬取了,另外防止造成一个死循环.举个例子 我爬www.baidu. ...

  2. [ python ] 项目二:主机批量管理程序

    开发要求: 1. 对主机进行批量管理    2. 可对单台或主机组批量执行命令    3. 可上传文件到对应的主机或组    4. 使用多线程实现  程序: 1. README # 作者:hkey # ...

  3. linux下用xampp安装php集成环境,并修改各自端口号

    一:安装xampp 1.到官网下载linux版xampp    https://www.apachefriends.org/zh_cn/index.html 下载后为:xampp-linux-x64- ...

  4. NOI2014 起床困难综合症 day1t1

    感觉NOI题在向简单方向发展,或者说明年会难到暴呢? 直接模拟啊,枚举每个二进制数位,看经过变换之后是否为1及为1的条件即可.\( O(nlogm)\). 然后...跪了一个点,第五个死活比标准大一. ...

  5. AC日记——[USACO06FEB]奶牛零食Treats for the Cows 洛谷 P2858

    [USACO06FEB]奶牛零食Treats for the Cows 思路: 区间DP: 代码: #include <bits/stdc++.h> using namespace std ...

  6. css3翻书效果

    强大的css3不需要解释,代码分层理解[直接复制],很有意思. 效果图: <ul class="align"> <li> <figure class= ...

  7. Kerberos KDC not reachable

    水贴..我这里出现这个问题的原因是服务器上安装的是 oracle  jdk, 但是没有安装 JCE 拓展包. 解决方式有两个: 1. 换成 openJDK 2. 安装上 JCE 拓展包 java 8 ...

  8. 设置iframe 载入页面的效果跟直接打开这个页面一样

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xht ...

  9. 【转载】文件下载FileDownloader

    原文地址:https://github.com/lingochamp/FileDownloader 特点 简单易用 高并发 灵活 可选择性支持: 独立/非独立进程 自动断点续传 需要注意 当下载的文件 ...

  10. ExtJs之列表(grid)

    --renderers渲染器 可以格式化该列显示的数据格式或者按照你自定义的脚本显示最终数据样子 先看下renderer: function()里的参数 renderer:function(value ...