iOS-Runtime、对象模型、消息转发
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、对象模型、消息转发的更多相关文章
- iOS Runtime的消息转发机制
前面我们已经讲解Runtime的基本概念和基本使用,如果大家对Runtime机制不是很了解,可以先看一下以前的博客,会对理解这篇博客有所帮助!!! Runtime基本概念:https://www.cn ...
- runtime之消息转发
前言 在上一篇文章中我们初尝了runtime的黑魔法,可以在程序编译阶段就获取到成员变量的名字,特性以及动态的给对象增加属性等等,在接下来中我们进一步了解OC的消息发送机制.如果之前没接触过runti ...
- OC:浅析Runtime中消息转发机制
一.介绍 OC是一门动态性语言,其实现的本质是利用runtime机制.在runtime中,对象调用方法,其实就是给对象发送一个消息,也即objc_msgSend().在这个消息发送的过程中,系统会进行 ...
- iOS开发·runtime原理与实践: 消息转发篇(Message Forwarding) (消息机制,方法未实现+API不兼容奔溃,模拟多继承)...
本文Demo传送门: MessageForwardingDemo 摘要:编程,只了解原理不行,必须实战才能知道应用场景.本系列尝试阐述runtime相关理论的同时介绍一些实战场景,而本文则是本系列的消 ...
- iOS的消息转发机制详解
iOS开发过程中,有一类的错误会经常遇到,就是找不到所调用的方法,当然这类问题比较好解决,给当前对象或其父类对象添加该方法即可,使得编译器在编译时能正确找到该方法:或者,还有另外的方法,由于Objec ...
- iOS消息转发
消息转发是一种功能强大的技术,可以大大增加Objective-C的表现力.什么是消息转发?简而言之,它允许未知的消息被困住并作出反应.换句话说,无论何时发送未知消息,它都会以一个很好的包发送到您的 ...
- Runtime 运行时之一:消息转发
解释一 上一篇文章咱们提到了Runtime的消息传递机制,主要围绕三个C语言API来展开进行的.这篇文章我将从另外三个方法来描述Runtime中另一个特性:消息转发机制. 一.消息转发机制 当向某个对 ...
- objc_msgSend消息传递学习笔记 – 消息转发
该文是 objc_msgSend消息传递学习笔记 – 对象方法消息传递流程 的基础上继续探究源码,请先阅读上文. 消息转发机制(message forwarding) Objective-C 在调用对 ...
- iOS runtime探究(二): 从runtime開始深入理解OC消息转发机制
你要知道的runtime都在这里 转载请注明出处 http://blog.csdn.net/u014205968/article/details/67639289 本文主要解说runtime相关知识, ...
随机推荐
- vue做购物车
写一点废话,昨天敲代码找bug,找了好久都没找到,后来一哥们找到他说,找代码的bug就像男女朋友吵架,女问男你错了没,男说错啦,女再问错哪了,男傻眼了不知道错哪.在找代码的过程中一直知道我错啦就是找不 ...
- ado中dispose和close的区别,摘自网络
Close() and Dispose() are basically the same thing on an ADO.NET connection object for providers shi ...
- tornado 模版继承 函数和类的调用
模版继承.函数和类的调用 目录结构 lesson5.py # -*- coding:utf-8 -*- import tornado.web import tornado.httpserver imp ...
- MySQL取得某一范围随机数(MySQL随机数)
若要在i ≤ R ≤ j 这个范围得到一个随机整数R ,需要用到表达式 FLOOR(i + RAND() * (j – i + 1)). 例如, 若要在7 到 12 的范围(包括7和12)内得到一个随 ...
- [ Python ] 文件的读写操作
1. 文件读写操作 读写文件是最常见的 IO 操作, Python 内置了读写文件的函数.在磁盘上读写文件的功能是由操作系统提供的,所以读写文件是请求操作系统打开一个文件对象(通常称为文件描述符),然 ...
- memcache和redis的对比
1.memcache a.Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态.数据库驱动网站 ...
- [转载]Python命令行参数学习
转载自: http://blog.163.com/weak_time/blog/static/25852809120169333247925/ Python的命令行参数,提供了很多有用的功能,可以方便 ...
- windows10 自带的OpenSSH Client(Beta)
我不知道其他版本有没有 ,我是windows10 专业版,版本1709,OS内部版本16288.1 安装过程: 1.我的电脑上面的卸载或更改程序 2.管理可选功能 3.添加功能 4.重启电脑,搞定 O ...
- 微软企业库5.0 学习之路——第八步、使用Configuration Setting模块等多种方式分类管理企业库配置信息
在介绍完企业库几个常用模块后,我今天要对企业库的配置文件进行处理,缘由是我打开web.config想进行一些配置的时候发现web.config已经变的异常的臃肿(大量的企业库配置信息充斥其中),所以决 ...
- php导出excel时间错误(同一个时间戳,用date得到不同的时间)
通过在date之前设置时区解决了 date_default_timezone_set("Asia/Shanghai"); $schedule_time = date("Y ...