本篇主要讲述在 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. AngularJS之Directive(三)

    前言 angular核心部分如下图几大块,最重要的莫过于指令这一部分,本文将重点讲解指令这一部分,后续笔者将通过陆续的学习来叙述其他如:factory.service等,若有叙述错误之处,欢迎各位指正 ...

  2. xUnit安装及注意事项

    前言 对于单元测试,想必大家都已再熟悉不过了,同时单元测试的重要性也越发突出,在招聘中也特别强调单元测试,但是对于微软内置的单元测试还是太过于繁琐,于是都在寻找一种简洁并且更加轻量的测试工具.用的最多 ...

  3. 安装infer整个过程

    日期:2015-06-26 孟起  15:43:25 大神.. 孟起  15:43:38 我是不是照着这个安装 HelloWorld  15:45:05 直接找二进制文件安卓就行 孟起  15:46: ...

  4. 相克军_Oracle体系_随堂笔记011-事物

    数据库主要实现的功能无非是以下三点: ①数据的一致性, ②数据的安全, ③数据的优化.   事物主要影响数据的一致性. 1.事务的基本概念    一组DML语句    insert.delete.up ...

  5. Spring加载xsd引起的问题小记

    前言 最近要把之前写好的监控系统加上报警功能,就是通过rpc调用发短信发邮件的服务发送报警信息.发短信发邮件的功能是通过dubbo管理提供的.自然使用这些服务就难免用到spring.而我这又是一个st ...

  6. QRCode

    这个星期, 领导要我总结项目中用到的一些技术, 然后交付文档. 嘿嘿, 奉命整理. 二维码, 相信很多项目中都会要求生成这个, 然后由手机端去扫描, 或存储一些详情信息, 或存储一条链接, 可以快捷访 ...

  7. C#基础-FileStream实现多线程断点续传

    一.前言 网上有许多的多线程断点续传操作,但总是写的很云里雾里,或者写的比较坑长.由于这几个月要负责公司的在线升级项目,所以正好顺便写了一下 代码如下: using System; using Sys ...

  8. Visual Studio.NET单选题

     在Visual Studio.NET窗口中,在__________窗口中可以察看当前项目的类和类型的层次信息. A. 解决方案资源管理器 B. 类视图 C. 资源视图 D. 属性 在线答题:http ...

  9. [Access] C# 通过 COM 组件访问 Access 文件

    说明: 1,采用 dynamic 调用 COM 组件,适用于 .NET 4.0 以上支持 dynamic 版本的才可以: 2,执行速度不敢恭维,只是因为要用于 Silverlight OOB 模式中才 ...

  10. javascript的 Object 和 Function

    一. javascript 的 内置对象: Object 和 Function javascript所有东西,包括 Function 都是对象 . Array  其实是一个 Function 类型的对 ...