本篇主要讲述在 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机制深入解析的更多相关文章

  1. iOS开发之runtime运行时机制

    最近参加三次面试都有被问到runtime,因为不太懂runtime我就只能支支吾吾的说点零碎.我真的好几次努力想看一看runtime的知识,因为知道理解它对理解OC代码内部变化有一定帮助,不过真心觉得 ...

  2. iOS开发之Runtime使用

    runtime简介 RunTime简称运行时.OC就是运行时机制,也就是在运行时候的一些机制,其中最主要的是消息机制. 对于C语言,函数的调用在编译的时候会决定调用哪个函数. 对于OC的函数,属于动态 ...

  3. iOS开发之Runtime常用示例总结

    经常有小伙伴私下在Q上问一些关于Runtime的东西,问我有没有Runtime的相关博客,之前还真没正儿八经的总结过.之前只是在解析第三方框架源码时,聊过一些用法,也就是这些第三方框架中用到的Runt ...

  4. iOS开发之Swift 4 JSON 解析指南

    Apple 终于在 Swift 4 的 Foundation 的模块中添加了对 JSON 解析的原生支持. 虽然已经有很多第三方类库实现了 JSON 解析,但是能够看到这样一个功能强大.易于使用的官方 ...

  5. iOS开发之Alamofire源码解析前奏--NSURLSession全家桶

    今天博客的主题不是Alamofire, 而是iOS网络编程中经常使用的NSURLSession.如果你想看权威的NSURLSession的东西,那么就得去苹果官方的开发中心去看了,虽然是英文的,但是结 ...

  6. iOS开发之runtime的运用-获取当前网络状态

    之前写过runtime的一些东西,这次通过runtime获取一些苹果官方不想让你拿到的东西,比如,状态栏内部的控件属性.本文将通过runtime带你一步步拿到状态栏中显示网络状态的控件,然后通过监测该 ...

  7. iOS开发之Alamofire源码解析

    今天博客中的Alamofire源码的版本是以3.4版本为例.上篇博客系统的对NSURLSession相关的东西进行了详细的解析,详情请看<详解NSURLSession>,为了就是给本篇博客 ...

  8. iOS开发之Runtime函数

    1.可以通过NSObject的一些方法获取运行时信息或动态执行一些消息: 1./*Returns a Boolean value that indicates whether the receivin ...

  9. 李洪强iOS开发之RunLoop的原理和核心机制

    李洪强iOS开发之RunLoop的原理和核心机制 搞iOS之后一直没有深入研究过RunLoop,非常的惭愧.刚好前一阵子负责性能优化项目,需要利用RunLoop做性能优化和性能检测,趁着这个机会深入研 ...

随机推荐

  1. Cmd Markdown编辑器简明语法手册

    标签: Cmd-Markdown 1. 斜体和粗体 使用 * 和 ** 表示斜体和粗体. 示例: 这是 斜体,这是 粗体. 2. 分级标题 使用 === 表示一级标题,使用 --- 表示二级标题. 示 ...

  2. # Hawk:开源贡献计划,设计,反思

    Hawk在发布之后,收到了不少朋友的感谢和使用反馈,沙漠君表示非常开心.软件肯定有很多的问题和不足,还有很多可扩展的空间,因此我希望更多的朋友,能够参与到改进Hawk的计划中来,为开源世界作出努力. ...

  3. EntityFramework 7 Linq Contains In 奇怪问题(已修复)

    问题说明: 博客问题纪录 Use EF7, Linq Contains In is error. EF7 Code Commit adding (client side) support for Co ...

  4. 【Java心得总结二】浅谈Java中的异常

    作为一个面向对象编程的程序员对于 下面的一句一定非常熟悉: try { // 代码块 } catch(Exception e) { // 异常处理 } finally { // 清理工作 } 就是面向 ...

  5. Jackson的简单用法

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 1简介 Jackson具有比较高的序列化和反序列化效率,据测试,无论是 ...

  6. Circuit Breaker Pattern(断路器模式)

    Handle faults that may take a variable amount of time to rectify when connecting to a remote service ...

  7. 【十大经典数据挖掘算法】kNN

    [十大经典数据挖掘算法]系列 C4.5 K-Means SVM Apriori EM PageRank AdaBoost kNN Naïve Bayes CART 1. 引言 顶级数据挖掘会议ICDM ...

  8. .NET 扩展方法 (二)

    上一篇随笔 .NET 扩展方法 (一) 已经对 扩展方法有了大致的介绍,这篇算是一个补充,让我们来看一下扩展方法的几个细节: 一.扩展方法具有继承性 当使用扩展方法扩展一个类型的时候,其也扩展了派生类 ...

  9. 用c#开发微信(5)自定义菜单设置工具 (在线创建)

    读目录 1 使用 2 原理 3. 错误 上次写了<用c#开发微信 (4) 基于Senparc.Weixin框架的接收事件推送处理 (源码下载)>,有园友问到如何创建菜单的问题,今天就介绍下 ...

  10. 关于tomcat文件下载配置

    前言 tomcat文件下载 关闭tomcat目录列表浏览功能 Tomcat 不能下载带中文文件名的附件的方法 在Java Web项目中文件下载是一个很常见的功能,最近在做项目中发现可以通过tomcat ...