文档章节

ios runtime基础知识

S
 Snaiper
发布于 2016/02/25 10:56
字数 2177
阅读 148
收藏 2

与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)


为什么叫消息呢?因为面向对象编程中,对象调用方法叫做发送消息。在编译时,应用的源代码就会被编将对象发送消息转换成runtime的objc_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 objects 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:其它普通的消息都会通过该函数来发送
  • objcmsgSendstret:消息中需要有数据结构作为返回值时,会通过该函数来发送消息并接收返回值
  • objcmsgSendSuper:与objcmsgSend函数类似,只是它把消息发送给父类实例
  • objcmsgSendSuperstret:与objcmsgSendstret函数类似,只是它把消息发送给父类实例并接收数组结构作为返回值

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

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函数即可。

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

image

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

Message Forwarding


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

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

image

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

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

image

当对象查询不到相关的方法,消息得不到该对象处理,会启动“消息转发”机制。消息转发还分为几个阶段:先询问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)

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


© 著作权归作者所有

S
粉丝 13
博文 100
码字总数 135194
作品 0
深圳
私信 提问
ArcGIS for iOS 开发系列(2) – 开发环境配置

我们已经掌握了一些基本知识,在动手编程之前,还需先配置下开发环境: 2.1 iOS 配置 1) Mac电脑(MacBook Air/ Pro、Mac mini和iMac都可以); 2) Snow Leopard(小版本10.6.2以上)、Lio...

长平狐
2012/11/28
1K
0
新鲜出炉的腾讯音乐iOS面试题

前言 为防止背题,大部分题目不设标准答案,重点考察面试者的基础知识和思维逻辑,答案的提示见后面。 正文 题目1、举例两个遇到过印象深刻的外网Crash,并介绍如何发现、定位、解决; 题目2...

落影loyinglin
2018/07/16
0
0
只会画界面写业务的 iOS 开发真的没人要了! | 程序员有话说

如果你是一个 iOS 开发的新手,我可以告诉你,这个职业的红利期早已过去。随着 APPL 的股价大跌以及内忧外患不断,iOS 这个职业的恐慌指数在不断的增高,已经对新手不太友好了。 如果你是一个...

iOS开发_小迷糊
05/29
0
0
General APIs mapping (通用 API mapping)

1、Android到Windows的API mappings 对于Android开发者,同样可以在API mapping 索引中找到映射,与使用iOS映射的方式一样。 我们提供了一个 API 映射索引来帮助你找到与 Windows 运行时 AP...

失足处男的倒霉孩子
2013/12/25
272
0
那些在学习iOS开发前就应该知道的事(part 1)

英文原文:Things I wish I had known before starting iOS development—Part 1 设计师设计出来了一个不错的引导界面,然而当我看到设计稿的时候,我们的app也没几天就要上线了。这个界面模...

TomatosX
2015/06/12
111
0

没有更多内容

加载失败,请刷新页面

加载更多

vue 2打包注意点

使用npm run build打包之后往往直接本地运行,路径类似这样:http://127.0.0.1:5500/xa/dist/index.html 或者http://127.0.0.1:5500/dist/index.html。然后页面打开是空白的,打开控制台查看...

牧云橙
28分钟前
4
0
归并排序

1.原理图 2.代码 public static void merge(int []a,int left,int mid,int right){ int []tmp=new int[a.length];//辅助数组 int p1=left,p2=mid+1,k=left;//p1、p2是检测......

wen123
31分钟前
4
0
css实现透明的两种方法

一、opacity:0~1 值越高,透明度越低: div{opacity:0.5 } 选择器匹配到的节点们,包括节点们的孩子节点,都会实现%50透明,另 0.5 可直接写成 .5 二、rgba(0~255,0~255,0~255,0~1) r...

Bing309
34分钟前
4
0
Tomcat 配置访问路径

此处只是部署完成后idea打开的默认路径,并非项目部署路径, 此处才是项目实际部署路径,可以有多个项目部署路径,idea可以配置默认打开一个

Aeroever
37分钟前
4
0
将ApiBoot Logging采集的日志上报到Admin

通过ApiBoot Logging可以将每一条请求的详细信息获取到,在分布式部署方式中,一个请求可能会经过多个服务,如果是每个服务都独立保存请求日志信息,我们没有办法做到统一的控制,而且还会存...

恒宇少年
38分钟前
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部