iOS开发之Runtime机制深入解析
本篇主要讲述在 OC 开发中主要涉及到的运行时机制:
运行时的工作:
运行时在 OC 中的工作:OC 语言的设计模式决定了尽可能的把程序从编译和链接时推迟到运行时。只要有可能,OC 总是使用动态的方式来解决问题。这意味着 OC 语言不仅需要一个编译器,同时也需要一个运行时系统来执行编译好的代码。这儿的运行时系统扮演的角色类似于 OC 语言的操作系统,OC 基于该系统来工作。
运行时的简单应用:
OC 2.0运行时系统参考库描述了OC 运行库的数据结构和函数接口。程序可以通过这些接口来和 OC 运行时系统交互。例如:增加一个类或者方法,或者获得所有类的定义列表等。
运行时的两个版本:
OC 运行时系统有两个版本,早期版本主要应用于 OC1.0中,现行版本用于 OC2.0中,在早期版本中,如果改变了类中实例变量的布局,就必须重新编译该类的所有子类。在现行版本中,如果改变了类中实例变量的布局,无需重新编译该类的任何子类。早起版本一般用于 Max OS X 系统中32位程序,此外可视为全部是现行版本。
交互途径:
1、通过 OC 源代码:
当编译 OC 类和方法时,编译器为实现语句动态特性将自动创建一些数据结构和函数。运行时系统的主要功能就是根据源代码中的表达式发送消息。
2、通过 Foundation 框架中类 NSObject 的方法:
Cocoa 程序中绝大部分类都是 NSObject 类的子类,所以大部分都继承了 NSObject 类的方法,因而继承了 NSObject类的行为。然而某些情况下,NSObject 类仅仅定义了完成某件事情的模板,而没有提供所有需要的代码,某些 NSObject 的方法只是简单的从运行时系统中获得信息,从而允许对象进行一定程度的自我检查。如:class 返回对象的类:isKindOfClass:和 isMemberOfClass:则检查对象是否在指定的类继承体系中;respondsToSelector:检查对象能够相应指定的消息;conformsToProtocol:检查对象是否实现了指定协议类的方法;methodForSelector:则返回指定方法实现的地址。
3、直接通过调用运行时系统的函数:
运行时系统是一个公开接口的动态库,由一些数据结构和函数的集合组成,这些数据结构和函数的声明头文件在/usr/include/objc 中。这些函数支持用纯 C 的函数来实现和 OC 同样的功能。浪游一些函数构成了 NSObject 类方法的基础。这些函数使得访问运行时系统接口和提供开发工具成为可能。尽管大部分情况下他们在 OC 程序中不是必须的,但是有时候对于 OC 这样的程序来说某些函数是非常有用的。
运行时的消息机制:
1、获得方法地址:
避免动态绑定的唯一方法就是取得方法的地址,并且直接像函数调用一样调用它。当一个方法会被联系调用很多次,而且您希望节省每次调用方法都要发送消息的开销时,使用方法地址来调用方法就显得很有效。
利用 NSObject 类中的 methodForSelector:方法,可以获得一个指向方法实现的指针,并可以使用该指针直接调用方法实现。methodForSelector:返回的指针和赋值的变量类型必须完全一致,包括方法的参数类型和返回值类型都在类型识别的考虑范围中。
在指定的消息被重复发送很多次时,避免动态绑定将减少大部分消息的开销。
2、objc_msgSend 函数
在 OC 中,消息是直到运行的时候才和方法实现绑定的,编译器会把一个消息表达式转换成一个对消息函数objc_msgSend 的调用。该函数有两个主要参数:消息接收者和消息对应的方法名字(也就是方法的选标)。
objc_msgSend(receiver, selector),同时接收消息中的任意数目的参数:objc_msgSend(receiver, selector, arg1, arg2,...)
该消息函数做了动态绑定所需要的一切;找到对应的方法实现-->将消息接收者对象和参数传递给找到的方法实现-->将方法实现的返回值作为该函数的返回值返回
当对象收到消息时,消息函数首先根据该对象的 isa 指针找到该对象所对应的类的方法表,并从表中寻找该消息对应的方法选标,如果找不到,objc_msSend 将从父类中找,知道 NSObject 类。一旦找到了选标,objc_msgSend 则以消息接收者对象为参数调用,调用该选标对应的方法实现。这就是在运行时系统中选择方法实现的方式。在面向对象编程中一般称作方法和消息动态绑定的过程。
为了加快消息的处理过程,运行时系统通常会将使用过的方法选标和方法实现的地址放入缓存中。每个类都有一个独立的缓存,同时包括继承的方法和在该类中定义的方法。消息函数会首先检查消息接收者对象对应的类的缓存(理论上,如果一个方法被使用过一次,那么它很可能被再次使用)。如果在缓存中已经有了需要的方法选标,则消息仅仅比函数调用慢一点点。如果程序运行了足够长的时间,几乎每个消息都能在缓存中找到方法实现。程序运行时,缓存也将随着新的消息的增加而增加。
3、使用隐藏的参数
当 objc_msgSend 找到方法对应的实现时,它将直接调用该方法的实现,并将消息中所有的参数都传递给方法实现,同时,还将传递两个隐藏的参数:
1、接受消息的对象:可以通过 self 来引用消息接收者对象
2、方法选标:通过选标_cmd 来引用方法本身。
尽管这些参数没有被显示声明,但在源代码中仍然可以引用它们。
动态方法解析:
1、动态方法解析:
@dynamic property name; 表示编译器需动态的生成该属性对应的方法。
可以通过实现 resolveInstanceMethod:和 resolveClassMethod:来动态的实现给定选标的对象方法或者类方法。
OC 方法可以认为是至少有两个参数 self 和_cmd 的 C 函数。可以通过 class_addMethod 方法将一个函数加入到类的方法中,如下:
void dynamicMethodIMP(id self, SEL _cmd) {
// implementation...
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(resolveThisMethodDynamically)) {
class_addMethod([self class], sel, (IMP) dynamicMethodIMP, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
在进入消息转发机制之前,respondsToSelector:和 instancesRespondToSelector:会被首先调用。可以在这两个方法中为传进来的选标提供一个 IMP。如果您实现了 resolveInstanceMethod:方法但是仍然希望正常的消息转发机制进行,只需要返回 NO 就可以了。
2、动态加载
OC 程序可以在运行时链接和载入新的类和范畴类。新载入的类和在程序启动时载入的类并没有区别。
动态加载可以用在很多地方,例如,系统配置中的模块就是被动态加载的。
应用场景:
在 Cocoa 环境中,动态加载一般被用来对应用程序进行定制。可以在运行时加载其他程序员编写的模块(和 interface Build载入定制的调色板以及系统配置程序载入定制的模块类似)。这些模块通过许可的方式扩展了自身的程序,而无需自己来定义或者实现。自己提供了框架,二其他程序员提供了实现。
消息转发:
1、消息转发
如果一个对象收到一条无法处理的消息,运行时系统会在抛出错误前,给该对象发送一条 forwardInvocation:消息,该消息的唯一参数是个 NSInvocation 类型的对象,该对象封装了原始的消息和消息的参数。所以可以实现 forwardInvocation: 方法来对不能处理的消息做一些默认的处理,也可以以其它的某种方式来避免错误被抛出。
当一个对象没有响应的方法实现而无法响应某消息时,运行时系统将通过 forwardInvocation: 消息通知该对象。每个对象都从 NSObject 类中集成了 forwardInvocation: 方法。然而,NSObject 中的方法实现只是简单的调用了 doerNotRecognizeSelector:。 通过实现自己的 forwardInvocation: 方法可以在该方法实现中将消息转发给其他对象。
消息可以通过 invokeWithTarget:方法来转发:
- (void)forwardInvocation:(NSInvocation *)anInvocation {
if ([someOtherObject respondsToSelector:[anInvocation selector]])
[anInvocation invokeWithTarget:someOtherObject];
else
[super forwardInvocation:anInvocation];
}
forwardInvocation:方法就像一个不能识别的消息的分发中心,将这些消息转发给不同接收对象。它可以将一个消息翻译成另外一个消息,或者简单的“吃掉”某些消息,因此没有响应也没有错误。forwardInvocation:方法也可以对不同的消息提供同样的相应,这一切都取决于方法的具体实现。该方法所提供是将不通的对象链接到消息链的能力。
注意:forwardInvocation:方法只有在消息接受对象中无法正常响应消息时才会被调用。所以,如果您希望您的对象将 clickBtn:消息转发给其他对象,您的对象不能有 clickBtn:方法。否则,forwardInvocation:将不可能会被调用。
2、消息转发和多重继承
消息转发很像集成,并且可以用来在 OC 程序中模拟多重继承。一个对象通过转发来响应消息,看起来就像该对象从别的类那里借来了或者“继承”了方法实现一样。通过 forwardInvocation: 方法将一个类中的消息转发给另一个类.
3、消息代理对象
4、消息转发和类继承
iOS开发之Runtime机制深入解析的更多相关文章
- iOS开发之runtime运行时机制
最近参加三次面试都有被问到runtime,因为不太懂runtime我就只能支支吾吾的说点零碎.我真的好几次努力想看一看runtime的知识,因为知道理解它对理解OC代码内部变化有一定帮助,不过真心觉得 ...
- iOS开发之Runtime使用
runtime简介 RunTime简称运行时.OC就是运行时机制,也就是在运行时候的一些机制,其中最主要的是消息机制. 对于C语言,函数的调用在编译的时候会决定调用哪个函数. 对于OC的函数,属于动态 ...
- iOS开发之Runtime常用示例总结
经常有小伙伴私下在Q上问一些关于Runtime的东西,问我有没有Runtime的相关博客,之前还真没正儿八经的总结过.之前只是在解析第三方框架源码时,聊过一些用法,也就是这些第三方框架中用到的Runt ...
- iOS开发之Swift 4 JSON 解析指南
Apple 终于在 Swift 4 的 Foundation 的模块中添加了对 JSON 解析的原生支持. 虽然已经有很多第三方类库实现了 JSON 解析,但是能够看到这样一个功能强大.易于使用的官方 ...
- iOS开发之Alamofire源码解析前奏--NSURLSession全家桶
今天博客的主题不是Alamofire, 而是iOS网络编程中经常使用的NSURLSession.如果你想看权威的NSURLSession的东西,那么就得去苹果官方的开发中心去看了,虽然是英文的,但是结 ...
- iOS开发之runtime的运用-获取当前网络状态
之前写过runtime的一些东西,这次通过runtime获取一些苹果官方不想让你拿到的东西,比如,状态栏内部的控件属性.本文将通过runtime带你一步步拿到状态栏中显示网络状态的控件,然后通过监测该 ...
- iOS开发之Alamofire源码解析
今天博客中的Alamofire源码的版本是以3.4版本为例.上篇博客系统的对NSURLSession相关的东西进行了详细的解析,详情请看<详解NSURLSession>,为了就是给本篇博客 ...
- iOS开发之Runtime函数
1.可以通过NSObject的一些方法获取运行时信息或动态执行一些消息: 1./*Returns a Boolean value that indicates whether the receivin ...
- 李洪强iOS开发之RunLoop的原理和核心机制
李洪强iOS开发之RunLoop的原理和核心机制 搞iOS之后一直没有深入研究过RunLoop,非常的惭愧.刚好前一阵子负责性能优化项目,需要利用RunLoop做性能优化和性能检测,趁着这个机会深入研 ...
随机推荐
- 【记录】ASP.NET MVC View 移动版浏览的奇怪问题
ASP.NET MVC View 中的一段代码: <span id="span_Id">@Model.ID</span> 没什么问题吧,浏览器浏览正常,查看 ...
- T-Sql(五)xml操作
t-sql中的xml操作在我们平时做项目的过程中用的很少,因为我们处理的数据量很少,除非一些用到xml的地方,t-sql中xml操作一般用在数据量很大,性能优化的地方,当然我在平时做项目的时候也是没用 ...
- 把《c++ primer》读薄(4-1 c和c++数组)
督促读书,总结精华,提炼笔记,抛砖引玉,有不合适的地方,欢迎留言指正. c和c++的数组和指针都属于低级的复合数据类型,比如c++的数组,类似vector容器,指针类似迭代器.低级的数据类型优势是速度 ...
- 《Python Web 接口开发与测试》---即将出版
为什么要出这样一本书? 首先,今年我有不少工作是跟接口自动化相关的,工作中的接口自动化颇有成效. 我一直是一个没有测试大格局的人,在各种移动测试技术爆发的这一年,我却默默耕耘着自己的一亩三分地儿(We ...
- js实现可拖拽的div
前言 下午忙里偷闲想写一个可拖拽的例子,留在脑海里一直都是三个事件mouseDown,mouseUp,mouseMove, 但从没有动手实践过,今天想起了自己实践了并学习了张鑫旭的demo实现. 学习 ...
- 资源等待类型sys.dm_os_wait_stats
动态管理视图 sys.dm_os_wait_stats 返回执行的线程所遇到的所有等待的相关信息.可以使用该聚合视图来诊断 SQL Server 以及特定查询和批处理的性能问题. 列名 数据类型 说 ...
- springmvc4环境简单搭建和定时任务
之前复制粘贴创建了几个ssm的项目,然而回头让自己写的时候还是一头雾水,究其原因是spring的陌生.仅仅是写过几个helloworld而已.而且是照着写.我都不知道springmvc到底需要多少ja ...
- [Java 安全]消息摘要与数字签名
消息摘要 算法简述 定义 它是一个唯一对应一个消息或文本的固定长度的值,它由一个单向Hash加密函数对消息进行作用而产生.如果消息在途中改变了,则接收者通过对收到消息的新产生的摘要与原摘要比较,就可知 ...
- [Web API] Web API 2 深入系列(6) Model绑定(上)
目录 解决什么问题 Model元数据解析 复杂类型 ValueProvider ValueProviderFactory 解决什么问题 Model: Action方法上的参数 Model绑定: 对Ac ...
- .Net语言 APP开发平台——Smobiler学习日志:实现在手机上调用摄像头进行扫描
最前面的话:Smobiler是一个在VS环境中使用.Net语言来开发APP的开发平台,也许比Xamarin更方便 样式一 一.目标样式 我们要实现上图中的效果,需要如下的操作: 1.从工具栏上的&qu ...