runtime基础

前言

学习Objective-C的运行时Runtime系统是很有必要的。个人觉得,得之可得天下,失之则失天下。

Objective-C提供了编译运行时,只要有可能,它都可以动态地运作。这意味着不仅需要编译器,还需要运行时系统执行编译的代码。运行时系统充当Objective-C语言的操作系统,有了它才能运作。

运行时系统所提供功能是非常强大的,在实际开发中是经常使用到的。比如,苹果不允许我们给Category追加扩展属性,是因为它不会自动生成成员变量,那么我们通过运行时就可以很好的解决这个问题。另外,常见的模型转字典或者字典转模型,对象归档等。后续我们再来学习如何应用,本节只是讲讲理论。

与Runtime交互

Objective-C程序有有三种与runtime系统交互的级别:

  1. 通过Objective-C源代码
  2. 通过Foundation库中定义的NSObject提供的方法
  3. 通过直接调用runtime方法

通过Objective-C源代码

在大多数的部分,运行时系统会自动运行并在后台运行。我们使用它只是写源代码并编译源代码。当编译包含Objective-C类和方法的代码时,编译器会创建实现了语言动态特性的数据结构和函数调用。该数据结构捕获在类、扩展和协议中所定义的信息。

最重要的runtime函数是发消息函数,在编译时,编译器会转换成类似objc_msgSend这样的发送消息的函数。因此,我们通过写好源代码,编译器会自动帮助我们编译成runtime代码。

通过NSObject提供的方法

Cocoa编程中,大部分的类都继承于NSObject,也就是说NSObject通常是根类,大部分的类都继承于NSObject。有些特殊的情况下,NSObject只是提供了它应该要做什么的模板,却没有提供所有必须的代码。

有些NSObject提供的方法仅仅是为了查询运动时系统的相关信息,这此方法都可以反查自己。比如-isKindOfClass:-isMemberOfClass:都是用于查询在继承体系中的位置。-respondsToSelector:指明是否接受特定的消息。+conformsToProtocol:指明是否要求实现在指定的协议中声明的方法。-methodForSelector:提供方法实现的地址。

通过直接调用runtime函数

runtime库函数在usr/include/objc目录下,我们主要关注是这两个头文件:

1

2

3

4

#import <objc/runtime.h>

#import <objc/objc.h>

关于如何使用,后续的文章再细细讲解。

消息(Message)

为什么叫消息呢?因为面向对象编程中,对象调用方法叫做发送消息。在编译时,应用的源代码就会被编将对象发送消息转换成runtimeobjc_msgSend函数调用。

Objective-C,消息在运行时并不要求实现。编译器会转换消息表达式:

1

2

3

[receiver message];

在编译时会转换成类似这样的函数调用:

1

2

3

objc_msgSend(receiver, selector);

具体会转换成哪个,我们来看看官方的原话:

1

2

3

4

5

6

7

When it encounters a method call, the compiler generates a call to one of the

*  functions \c objc_msgSend, \c objc_msgSend_stret, \c objc_msgSendSuper, or \c objc_msgSendSuper_stret.

*  Messages sent to an object’s superclass (using the \c super keyword) are sent using \c objc_msgSendSuper;

*  other messages are sent using \c objc_msgSend. Methods that have data structures as return values

*  are sent using \c objc_msgSendSuper_stret and \c objc_msgSend_stret.

也就是说,我们是通过编译器来自动转换成运行时代码时,它会根据类型自动转换成下面的其它一个函数:

  • objc_msgSend:其它普通的消息都会通过该函数来发送
  • objc_msgSend_stret:消息中需要有数据结构作为返回值时,会通过该函数来发送消息并接收返回值
  • objc_msgSendSuper:与objc_msgSend函数类似,只是它把消息发送给父类实例
  • objc_msgSendSuper_stret:与objc_msgSend_stret函数类似,只是它把消息发送给父类实例并接收数组结构作为返回值

另外,如果函数返回值是浮点类型,官方说明如下:

1

2

3

4

5

6

7

8

9

* arm:    objc_msgSend_fpret not used

* i386:   objc_msgSend_fpret used for `float`, `double`, `long double`.

* x86-64: objc_msgSend_fpret used for `long double`.

*

* arm:    objc_msgSend_fp2ret not used

* i386:   objc_msgSend_fp2ret not used

* x86-64: objc_msgSend_fp2ret used for `_Complex long double`.

其实这是一个条件编译,我们不用担心是哪种处理器上,我们只需要调用objc_msgSend_fpret函数即可。

注意事项:一定要调用所调用的API支持哪些平台,乱调在导致部分平台上不支持而崩溃的。

当消息被发送到实例对象时,它是如何处理的:

我们的根类是NSObject,它会一层一层的传递,直接找到要处理该消息的对象,若都没有找到,正常情况下会出现Unreconized selector ...这样的崩溃提示了。

Message Forwarding

当发送消息给一个不处理该消息的对象是错误的。然后在宣布错误之前,运行时系统给了接收消息的对象处理消息的第二个机会。

当某对象不处理某消息时,可以通过重写-forwardInvocation:方法来提供一个默认的消息响应或者避免出错。当对象中找不到方法实现时,会按照类继承关系一层层往上找。我们看看类继承关系图:

所有元类中的isa指针都指向根元类,而根元类的isa指针则指向自身。根元类是继承于根类的,与根类的结构体成员一致,都是objc_class结构体,不同的是根元类的isa指针指向自身,而根类的isa指针为nil

我们再看看消息处理流程:

当对象查询不到相关的方法,消息得不到该对象处理,会启动“消息转发”机制。消息转发还分为几个阶段:先询问receiver或者说是它所属的类是否能动态添加方法,以处理当前这个消息,这叫做“动态方法解析”,runtime会通过+resolveInstanceMethod:判断能否处理。如果runtime完成动态添加方法的询问之后,receiver仍然无法正常响应则Runtime会继续向receiver询问是否有其它对象即其它receiver能处理这条消息,若返回能够处理的对象,Runtime会把消息转给返回的对象,消息转发流程也就结束。若无对象返回,Runtime会把消息有关的全部细节都封装到NSInvocation对象中,再给receiver最后一次机会,令其设法解决当前还未处理的这条消息。

提示:消息处理越往后,开销也就会越大,因此最好直接在第一步就可以得到消息处理。

我们看看类结构体:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

struct objc_class {

Class isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__

Class super_class                                        OBJC2_UNAVAILABLE;

const char *name                                         OBJC2_UNAVAILABLE;

long version                                             OBJC2_UNAVAILABLE;

long info                                                OBJC2_UNAVAILABLE;

long instance_size                                       OBJC2_UNAVAILABLE;

struct objc_ivar_list *ivars                             OBJC2_UNAVAILABLE;

struct objc_method_list **methodLists                    OBJC2_UNAVAILABLE;

struct objc_cache *cache                                 OBJC2_UNAVAILABLE;

struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;

#endif

} OBJC2_UNAVAILABLE;

/* Use `Class` instead of `struct objc_class *` */

我们可以看到每个类结构体都会有一个isa指针,它是指向元类的。它还有一个父类指针super_class,指针父类。包含了类的名称name、类的版本信息version、类的一些标识信息info、实例大小instance_size、成员变量地址列表ivars、方法地址列表methodLists、缓存最近使用的方法地址cache、协议列表protocols`。

我们在使用时,经常使用到Class,它就是:

1

2

3

typedef struct objc_class *Class;

当类为根类时,它的super_class就会是nil。普通的Class存储的是实例成员,如-号方法、属性、成员变量,而isa则指向元类,而元类存储的是静态成员,如+号方法、static成员。

Type Encoding

编码值 含意
c 代表char类型
i 代表int类型
s 代表short类型
l 代表long类型,在64位处理器上也是按照32位处理
q 代表long long类型
C 代表unsigned char类型
I 代表unsigned int类型
S 代表unsigned short类型
L 代表unsigned long类型
Q 代表unsigned long long类型
f 代表float类型
d 代表double类型
B 代表C++中的bool或者C99中的_Bool
v 代表void类型
* 代表char *类型
@ 代表对象类型
# 代表类对象 (Class)
: 代表方法selector (SEL)
[array type] 代表array
{name=type…} 代表结构体
(name=type…) 代表union
bnum A bit field of num bits
type A pointer to type
? An unknown type (among other things, this code is used for function pointers)

我们想要通过运行时处理各种类型,那么我们必须要知道哪种字符代表什么类型。

时间: 2024-08-07 00:14:15

runtime基础的相关文章

runtime基础知识

Runtime数据结构:在Objective-C中,使用[receiver message]语法并不会马上执行receiver对象的message方法的代码,而是向receiver发送一条message消息,这条消息可能由receiver来处理,也可能由转发给其他对象来处理,也有可能假装没有接收到这条消息而没有处理.其实[receiver message]被编译器转化为: id objc_msgSend ( id self, SEL op, ... ); 下面从两个数据结构id和SEL来逐步分析

iOS 运行时RunTime使用场景一:打点统计用户行为,深度解耦

转自:http://www.jianshu.com/p/0497afdad36d 用户统计.jpeg 用户行为统计(User Behavior Statistics, UBS)一直是移动互联网产品中必不可少的环节,也俗称埋点.在保证移动端流量不会受较大影响的前提下,PM们总是希望埋点覆盖面越广越好.目前常规的做法是将埋点代码封装成工具类,但凡工程中需要埋点(如点击事件.页面跳转)的地方都插入埋点代码.一旦项目越来越复杂,你会发现埋点的代码散落在程序的各个角落,不利于维护以及复用.本文旨在探讨利用

可复用且高度解耦的iOS用户统计实现

http://www.cocoachina.com/ios/20160421/15912.html 本文为投稿文章,作者:编程小翁(简书) 用户统计 用户行为统计(User Behavior Statistics, UBS)一直是移动互联网产品中必不可少的环节,也俗称埋点.在保证移动端流量不会受较大影响的前提下,PM们总是希望埋点覆盖面越广越好.目前常规的做法是将埋点代码封装成工具类,但凡工程中需要埋点(如点击事件.页面跳转)的地方都插入埋点代码.一旦项目越来越复杂,你会发现埋点的代码散落在程序

【二】D语言现状、基本规定和相关资源介绍

这算是本系列文章的一个序吧,主要是为以后的学习做铺垫,文本分为三个部分,第一部分是对于网上一些比较旧的资料的问题的一些更正,当然我也不可能看过所有的资料,难免会有遗漏.第二部分是D语言最基本的规定,第三部分是相关的资源 一.对于一些网上旧资料上的内容不准确之处在此指出 1.D对闭包的支持不好 目前版本支持完全的闭包,所以不用担心 2.D不支持动态链接 目前版本支持动态链接技术,但Windows平台需要注意的是D的runtime建立在dmc(一个C编译器)runtime 基础上,它与VC的二进制格

java web 开发三剑客 -------电子书

Internet,人们通常称为因特网,是当今世界上覆盖面最大和应用最广泛的网络.根据英语构词法,Internet是Inter + net,Inter-作为前缀在英语中表示“在一起,交互”,由此可知Internet的目的是让各个net交互.所以,Internet实质上是将世界上各个国家.各个网络运营商的多个网络相互连接构成的一个全球范围内的统一网,使各个网络之间能够相互到达.各个国家和运营商构建网络采用的底层技术和实现可能各不相同,但只要采用统一的上层协议(TCP/IP)就可以通过Internet

.Net Core 学习笔记1&mdash;&mdash;包、元包、框架

.Net Core 是由NuGet包(package)组成的平台. 一起使用的多个包的集合:元包(Metapackage) package 包 (对应以前的程序集概念) Framework 框架 assembly 程序集 重要的NuGet包: System.Runtime 基础包 System.Collections 泛型集合 System.Net.Http 用于HTTP网络通信 System.IO.FileSystem 读写磁盘 System.Linq 查询对象 System.Reflecti

一键发布部署vs插件[AntDeploy],让net开发者更幸福

一键发布工具(ant deploy tool) 插件下载地址: https://marketplace.visualstudio.com/items?itemName=nainaigu.AntDeploy 1.iis一键发布自动部署 (iis deploy support) 支持netcore 和 netframework发布 (支持mvc webapi) 支持website自动创建 ps:需要在windows 服务器上安装一个agent 具体请看下面有说明 2.docker一键发布自动部署 (

ArcGIS Runtime for Android开发教程V2.0(3)基础篇---Hello World Map

原文地址: ArcGIS Runtime for Android开发教程V2.0(3)基础篇---Hello World Map - ArcGIS_Mobile的专栏 - 博客频道 - CSDN.NET http://blog.csdn.net/arcgis_mobile/article/details/8124005 通过上面章节,我们已经了解如何配置一个ArcGIS Runtime for Android开发环境,下面我们将介绍如何使用Eclipse创建一个ArcGIS移动项目Hello W

2.Java基础之Runtime对象

毕向东老师Java基础学习笔记——Runtime对象 今天学习Java中的Runtime对象后,感觉这个对象对我们主要有以下几点用处. 1.使用java代码打开本地可执行文件,比如打开一个计算器. 2.打开一个程序,并用该程序打开一个支持的文件. 比如:1.打开记事本,用记事本打开*.java文件, 2.打开暴风影音播放器,用播放器打开一个本地视频. 范例代码如下: /************************************** Runtime对象: 1.该类并没有提供构造函数.