美文网首页iOS痛苦runtimeiOS开发
《精通Objective-C》 运行时(Runtime) 阅读笔

《精通Objective-C》 运行时(Runtime) 阅读笔

作者: dibadalu | 来源:发表于2016-01-22 16:36 被阅读1105次

前言

学习iOS开发已经几个月了,越学越觉得Objective-C的水不浅啊。有关这门语言,我还有很多知识点需要恶补。本来还在想要不要去学swift,感觉2016年swift会更火。现在觉得Objective-C都没学好,还想学swift。做梦吧。Swift虽然语法变得简单了,但是同时多了不少特性,相对Objective-C来说,深入学习的坑一点都不浅。
为了打好基础,从图书馆里借了《精通Objective-C》,此书主要讲述Objective-C的运行时环境、Foundation框架等底层知识,我直接跳读了有关运行时系统的3章——第7、8和9章。

个人阅读技术书的步骤是:

  • 整体阅读完一章内容,并用铅笔做些笔记
  • 把书里的代码示例亲自打一遍,并做好注释和小结
  • 对不懂的内容多次阅读
  • 做小结,记录关键知识点

这样做的原因是确保自己在忘记部分知识点的时候,通过自己的代码示例和笔记可以快速查漏补缺,不用重新拿起技术书。(ps:最重要的原因是书是从图书馆借的,下次可不一定能借到)

Demo

RuntimeLearningDemo 提取码: 95nu

第7章.运行时系统

问题1 运行时系统的运行方式

当源代码被编译时,编译器(运行时系统的组成部分)会创建数据结构好函数调用语句,使用它们以动态方式将接收器(类/对象)和消息选择器与方法的实现代码对应起来。在执行程序时,运行时系统库(运行时系统的另外一个组成部分)利用这些信息找到并调用适当的方法。

问题2 运行时系统实现对象消息传递的方式

编译器会将[接收器 消息]形式的对象消息,转换为声明中含有方法签名的(ANSI)C函数调用语句。因此,为了生成正确的对象消息传递代码,编译器需要获得选择器值和方法签名。选择器可以从对象消息表达式提取,而方法签名的匹配最好确保拥有不同特征的方法也拥有不同的名称。

问题3 使用动态类型的好处

  • 使用动态类型可以简化类接口,无须为不同输入参数类型编写不同的方法声明。
  • 动态类型还可以提供非常大的灵活性,在执行程序的过程中改进程序使用的数据类型,并在不重新编译和重新部署的情况下引入新的数据类型。

问题4 NSObject类的API中用于执行对象内省的方法

  • isKindOfClass:判断某个对象是否是该类或其子类的实例
  • respondsToSelector:判断某个对象是否会对选择器作出回应
  • conformsToProtocol:判断某个对象是否遵守指定的协议
  • methodSignatureForSelector:为选择器提取方法签名

本章要点

Objective-C运行时系统的特性和关键组件:

  • 使用Objective-C中的消息传递(对象消息传递)特性可以调用类和对象中的方法。对象消息传递是一种动态特性。


    对象消息传递
  • 消息传递表达式包含接收器(接收器的对象/类)和消息,而消息又由选择器和相应的输入参数构成。
  • 选择器是一种分段的文本字符串,每个字段以冒号结尾并且后跟参数。
  • 选择器数据类型(SEL)是一种特殊的Objective-C数据类型,它用于在编译源代码时使用具有唯一性的标识符替换选择器。使用@selector关键字或Foundation框架中的NSSelectorFronString()函数,可以创建类型为SEL的变量。
  • 方法签名(method signture)定义了方法的输入参数和返回值的数据类型。
  • 使用动态类型(dynamic typing)功能可以在运行程序时决定对象的类型。Objective-C通过id数据类型支持动态类型。
  • 动态绑定(dynamic binding)是指在程序运行时(而不是编译时),将消息与方法对应起来的处理过程。动态绑定功能实现了OOP的多态性,并且能够在不影响已有代码的情况下,将新对象和代码添加到程序中,从而降低了对象之间的耦合度。


    动态绑定
  • 使用动态方法决议能够以动态方式实现方法。使用@dynamic指令可以告知编译器与某个属性关联的方法能够以动态方式实现。NSObject类中含有resolveInstanceMethod:和resolveClassMethod:方法,使用它们能够以动态方式分别实现由选择器指定的实例和类方法。
  • 使用动态加载功能可以根据需要加载Objective-C程序的可执行代码和源代码,而无须在启动应用程序时加载它的所有组件。
  • Foundation框架中NSObject类的API含有非常多用于执行对象内省的方法。在运行程序时,这些API能够以动态方式查询方法的信息,还可以测试对象的继承性、行为和一致性。

第8章.运行时系统的结构

问题1 编译器如何为Objective-C类和对象生成可执行代码,以及它如何实现对象消息

  1. 当编译器解析对象消息(发送消息的表达式),如[接收器 消息],它会生成调用运行时系统库中函数objc_msgSend()的代码。每条消息都是以动态方式处理的。对于源代码中的类和对象来说,编译器创建了执行对象消息操作所需的数据结构。
  2. 当编译器解析含有类定义和对象的Objective-C源代码时,它会生成相应的运行时数据结构。
    Objective-C中的类与运行时系统库中的Class数据类型对应。Class数据类型是指向objc_class标识符的不透明数据类型的指针。
    如typedef struct objc_class *Class
    使用运行时系统库中的函数可以访问Class(即objc_class)数据类型的变量。
    编译器在解析Objective-C对象的源代码时,会生成创建运行时对象类型的可执行代码。

问题2 Objective-C对象和类的运行时数据结构

实际上,所有Objective-C对象和类的运行时类型都是以isa指针开头的。

  • 与Objective-C对象对应的运行时数据结构——objc_object数据类型是一种带objc_object标识符的C语言结构,如下:


    objc_object数据结构
  • 与Objective-C中id数据类型对应的运行时数据类型也是一种C语言结构,该结构被定义为指向objc_object的指针,如下:

id运行时数据类型
  • Objective-C块对象也拥有相应的运行时数据结构

问题3 Objective-C运行时系统的数据类型和函数

*** 数据类型***

  • 类定义数据结构(类、方法、实例变量、分类、IMP和SEL)
  • 实例数据类型(id、objc_object和objc_super)
  • 值(BOOL)

*** 函数***

  • 对象消息
  • 类函数
  • 实例函数
  • 协议函数
  • 方法函数
  • 属性函数
  • 选择器函数

*** 布尔型常量(YES和NO)***
*** 空值(NULL、nil和Nil)***

问题4 Objective-C运行时系统的函数及其含义

函数 含义
objc_getClass 对象的类定义
class_getSuperClass 类的父类
objc_getMetaClass 对象的元类定义
class_getName 类的名称
class_getVersion 类的版本信息
class_getInstanceeSize 以字节为单位的类尺寸
class_copyIvarList 类的实例变量列表
class_copyMethodList 类的方法列表
class_copyProtocolList 类的协议列表
class_copyPropertyList 类的属性列表

问题5 运行时系统中的消息传递操作

消息传递操作

注意:

  • 某个类通过父类指针指向它的父类,元类也通过其父类指针指向其对应类父类的元类
  • 基类(NSObject类)的元类会使其父类指针指向该基类本身
  • 对象的isa变量会指向描述该对象的类
  • 类(对象)的isa变量会指向描述类(及其类方法等)的元类
总结
  • 当源代码向对象发送消息时,运行时系统会通过相应的类实例方法虚函数表,获得合适的实例方法实现代码并跳转执行该方法
  • 当源代码向类发送消息时,运行时系统会通过该类的元类类方法虚函数表,获得合适的类方法实现代码并跳转执行该方法

问题6 运行时系统库用于实现对象消息传递的设计机制

  • 通过虚函数表查找方法
  • 通过dyld共享缓存使选择器拥有唯一性
  • 消息分派
  • 访问类实例方法

问题7 运行时系统库中的方法数据类型

方法数据类型

问题8 运行时系统的方法查询和调用机制——虚函数表

虚函数表是一个用于存储IMP类型(Objective-C方法的实现代码)数据的数组。

  • 每个运行时系统类实例(objc_class)都有一个指向虚函数表的指针
  • 每个类实例还拥有最近调用过方法的指针缓存
    具体流程如下:
运行时系统的方法查询逻辑

问题9 dyld

dyld是一种系统服务,用于定位和加载动态库。

  • 含有共享缓存,能够使多个进程共用这些动态库
  • dyld共享缓存中还含有一个选择器表,从而使运行时系统能够通过该缓存访问共享库和自定义的选择器。
    因此,运行时系统能够通过dyld共享缓存获取共用库的选择器,而且需要做的仅是为应用中的自定义类更新选择器。

问题10 消息分派

objc_msgSend()函数一定会寻找与消息指定的接收器和选择器对应的IMP指针,然后跳转到该指针指向的地址执行此处的代码。完成该操作后,运行时系统就会使用已优化的自定义汇编语言代码分派方法。这段代码有时叫蹦床,作用是找到正确的代码并跳转过去。

问题11 访问类实例方法

  • Objective-C中的类也是对象,也能接受消息,如
    [NSObject alloc]
  • 运行时系统是通过元类实现向类发送消息的功能。
  • 元类是一种特殊的类对象,运行时系统使用其中含有的信息能够找到并调用类方法。

问题12 与运行时系统交互

Objective-C程序通过与运行时系统交互实现动态特性。


与运行时系统交互

问题13 NSObject类的运行时方法

运行时系统API是使用C语言编写的,因此,Foundation框架中的NSObject类提供了一系列封装运行时系统API功能的方法,这些运行时方法所提供的功能:

  • 对象内省
  • 消息转发
  • 动态方法决议
  • 动态加载

本章要点

介绍运行时系统结构中的关键组成部分,深入理解运行时系统实现面向对象特性和动态功能的方式:

  • Objective-C运行时系统由两个主要部分构成:编译器和运行时系统库。编译器会接受Objective-C源代码并生成由运行时系统库执行的代码。运行时系统库会与所有Objective-C程序链接(在链接阶段)。这两个关键组成部分一起实现了Objective-C语言的面向对象特性和动态功能。
  • 运行时系统库API定义了一系列数据类型、函数和常量。运行时系统库的公用API是使用C语言编写的。
  • 运行时系统库实现了各种特性和机制,使用它们可以增强应用程序的性能和可扩展性。典型的特性和机制包括方法缓存、虚函数表和dyld共享缓存。
  • 运行时系统库使用元类查找和调用类方法。元类是一种特殊的数据类型,运行时系统使用其中的信息可以查找和调用类方法。
  • Foundation框架中的NSObject类提供了一系列方法,使用它们可以调用运行时系统功能和行为。这些方法可以执行对象内省、动态方法决议、动态加载和消息转发操作。

后记

看完3章Runtime的内容,但是还是意味未尽。

  • 选择谷歌搜索


    Snip20160122_1.png
  • 或者,微博搜索

Snip20160122_2.png
  • 之后,按需选文。

粗略的翻看了搜索到的文章,感觉还是没有《精通Objective-C》讲的系统且全面,当然如果能耐心去看官方文档,那自然是最好的。网上的大多数文章要么是只是对Runtime某个特性大笔着墨,或者只是对知识点的查漏补缺。要想真正学习Objective-C的底层知识,还是得通过技术书系统的学,然后配合官方文档和WWDC视频补充。
Runtime的学习刚刚开始,剩下只有通过实践和阅读优秀的源码慢慢学习了。

参考文章

*** ibireme大神这两篇文章是通过苹果官方源代码注释并讲解的。 ***
Objective-C 中的类和对象 by ibireme

Objective-C 中的消息与消息转发 by ibireme

Objective-C与Runtime by 春哥

从AOP框架学习iOS Runtime by 林熠 from 阿里巴巴技术协会

Objective-C Runtime 消息机制 - 代码背后发生的事情

让你快速上手Runtime by 峥吖

相关文章

网友评论

  • 妙手空虚:哪里有这本书的epub格式电子版
  • 大刀阔斧007:mark 一下,学习了
  • NSBug:完全看不懂
    287f3680d02a:这本书我看过一遍,却没有感觉比看官方文档舒服、清晰。所以如果英文不错的话,建议还是直接看官方文档 https://developer.apple.com/reference/objectivec/1657527-objective_c_runtime?language=objc
    dibadalu:@孙琦 这是我看书写的笔记,给自己做备忘录用的。想弄明白这东西,还需要多看文档多实践。我现在自己也在学习中。加油吧!
  • B9班的真高兴:Mark一下,不错哦

本文标题:《精通Objective-C》 运行时(Runtime) 阅读笔

本文链接:https://www.haomeiwen.com/subject/mwfrkttx.html