本篇主要讲述在 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. (第六天)DOM

    概念 文档对象模型(DOM)是表示和操作HTML和XML文档内容的基础API. 选取文档元素 (1)通过ID选取元素 var id = document.getElementById("se ...

  2. iOS开发之遍历Model类的属性并完善使用Runtime给Model类赋值

    在上篇博客<iOS开发之使用Runtime给Model类赋值>中介绍了如何使用运行时在实体类的基类中添加给实体类的属性赋值的方法,这个方法的前提是字典的Key必须和实体类的Property ...

  3. hdu4831 Scenic Popularity(线段树)

    题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=4831 题目大概意思就是有多个风景区和休息区,每个风景区有热度,休息区的热度与最接近的分景区的热度相同, ...

  4. .Net中DataAdapter批量插入和更新数据总结

    前言 前段时间一直在忙着项目上线,在做项目的同时遇到了一些之前不曾碰到的问题,因为没有经验,只能从网上找一些相关的解决方案,但是网上提供的资料实在是太杂,有的根本不能用,耗时又耗力. 我希望把我这段时 ...

  5. Hammer.js分析(三)——input.js

    input.js是所有input文件夹中类的父类,浏览器事件绑定.初始化特定的input类.各种参数计算函数. Input父类和其子类就是在做绑定事件,各种参数计算.整合.设置等返回自定义事件对象,交 ...

  6. 组件化h5活动模板的实现

    需求: 实现一套灵活的活动组件模板,编辑人员只需要打开后台,拖拽相应组件,填入相应内容,最终就生成一个活动页面. 因为涉及投票,评论,关注等功能(每个功能都当做一个组件),所以一个富文本编辑器是无法实 ...

  7. 测试驱动开发与Python

    最近在看一本书<Test-Driven Development with Python>,里面非常详细的介绍了如何一步一步通过测试驱动开发(TDD)的方式开发Web项目.刚好这本书中使用了 ...

  8. DocX在C#中的基本操作方法

    用了一个星期把园子里2016年中有关.net的文章都看了,有些只是大致的看了一下,在看的同时也在记录一些通用的方法.发现有很多对NPOI的文档,主要是操作Excl的方法,却很少有关文档类型的方法. 在 ...

  9. 分享一下刚刚HP电话面试。。。。。。。。我估计我挂了,不过还是要来分享一下

    面试官是个中国人,给我是全英文面试,总之是做HP的外包业务,说得很好的工作环境,里面都是一些老外在工作. 首先是要用英文介绍了下自己,我自己觉得自己也还是不错的吧,然后就说了一通(其实我好久没说英文了 ...

  10. c#和Javascript中去重总结

    一.前言 去重在我们的开发过程中经常遇到,避免重复元素的添加,我们需要对获取到的集合(包括List.Array等) 做相关的过滤操作.确保对象的唯一性,从而达到保证数据不冗余重复.由于自己是做.net ...