runtime基础
前言
学习Objective-C
的运行时Runtime
系统是很有必要的。个人觉得,得之可得天下,失之则失天下。
Objective-C
提供了编译运行时,只要有可能,它都可以动态地运作。这意味着不仅需要编译器,还需要运行时系统执行编译的代码。运行时系统充当Objective-C
语言的操作系统,有了它才能运作。
运行时系统所提供功能是非常强大的,在实际开发中是经常使用到的。比如,苹果不允许我们给Category
追加扩展属性,是因为它不会自动生成成员变量,那么我们通过运行时就可以很好的解决这个问题。另外,常见的模型转字典或者字典转模型,对象归档等。后续我们再来学习如何应用,本节只是讲讲理论。
与Runtime交互
Objective-C
程序有有三种与runtime
系统交互的级别:
- 通过
Objective-C
源代码 - 通过
Foundation
库中定义的NSObject
提供的方法 - 通过直接调用
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 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) |
我们想要通过运行时处理各种类型,那么我们必须要知道哪种字符代表什么类型。
runtime基础的更多相关文章
- runtime基础知识
看到一篇不错的runtime方面博客: 引言 相信很多同学都听过运行时,但是我相信还是有很多同学不了解什么是运行时,到底在项目开发中怎么用?什么时候适合使用?想想我们的项目中,到底在哪里使用过运行时呢 ...
- 【原】iOS动态性(五)一种可复用且解耦的用户统计实现(运行时Runtime)
声明:本文是本人 编程小翁 原创,转载请注明. 为了达到更好的阅读效果,强烈建议跳转到这里查看文章. iOS动态性是我的关于iOS运行时的系列文章,由浅入深,从理论到实践.本文是第5篇.有兴趣可以看看 ...
- 《玩转D语言系列》二、D语言现状、基本规定和相关资源介绍
这算是本系列文章的一个序吧,主要是为以后的学习做铺垫,文本分为三个部分,第一部分是对于网上一些比较旧的资料的问题的一些更正,当然我也不可能看过所有的资料,难免会有遗漏.第二部分是D语言最基本的规定,第 ...
- IOS面试攻略
IOS面试攻略(1.0) 2013-10-13 20:58:09| 分类: IOS面试 | 标签:ios知识点总汇 ios面试 |举报|字号 订阅 来自:伊甸网 @ 看到这个关键字,我 ...
- .Net Core 学习笔记1——包、元包、框架
.Net Core 是由NuGet包(package)组成的平台. 一起使用的多个包的集合:元包(Metapackage) package 包 (对应以前的程序集概念) Framework 框架 as ...
- 一键发布部署vs插件[AntDeploy],让net开发者更幸福
一键发布工具(ant deploy tool) 插件下载地址: https://marketplace.visualstudio.com/items?itemName=nainaigu.AntDepl ...
- Runtime-iOS运行时应用
本篇将会总结Rutime的具体应用实例,结合其动态特性,Runtime在开发中的应用大致分为以下几个方面(Runtime应用图): 相关文章:iOS运行时Runtime基础 一.动态方法交换:Meth ...
- 可复用且高度解耦的iOS用户统计实现
http://www.cocoachina.com/ios/20160421/15912.html 本文为投稿文章,作者:编程小翁(简书) 用户统计 用户行为统计(User Behavior Stat ...
- 2.Java基础之Runtime对象
毕向东老师Java基础学习笔记——Runtime对象 今天学习Java中的Runtime对象后,感觉这个对象对我们主要有以下几点用处. 1.使用java代码打开本地可执行文件,比如打开一个计算器. 2 ...
随机推荐
- 浏览器差异性hack
1 js函数 很多人误以为数组 push 方法拼接字符串会比 += 快,要知道这仅仅是 IE6-8 的浏览器下. 实测表明现代浏览器使用 += 会比数组 push 方法快,而在 v8 引擎中,使用 + ...
- 使用Angular构建单页面应用(SPA)
什么是SPA?看下图就是SPA: 下面说正经的,个人理解SPA就是整个应用只有一个页面,所有的交互都在一个页面完成,不需要在页面之间跳转. 单页面的好处是更快的响应速度,更流畅的用户体验,甚至和桌面应 ...
- 关于MTK平台SIM-ME Lock的配置方案
针对一些运营商的锁网需求,MTK平台已经对其有很好的支持.绝大多数的海外需求可以通过直接配置相关文件来完成.这里简单描述一下配置方法,不做原理分析. 相关数据结构分析: Modem中与SML锁网配置相 ...
- css格式布局
一.position:fixed 锁定位置(相对于浏览器的位置),例如有些网站的右下角的弹出窗口. 示例 : 二.position:absolute 1.外层没有position:absolute(或 ...
- python--随机函数(random,uniform,randint,randrange,shuffle,sample)
random() random()方法:返回随机生成的一个实数,它在[0,1)范围内 运用random()方法的语法: import random #random()方法不能直接访问,需要导入rand ...
- sftp配置多用户权限
sftp配置多用户权限 工作需要,用户上传文件到目录下,用ftp不太安全,选择sftp.让用户在自己的home目录下活动,不能ssh到机器进行操作. 下面开始干活. 查看ssh版本 ssh - ...
- Deep Learning(深度学习)网络资源
Deep Learning(深度学习) ufldl的2个教程(这个没得说,入门绝对的好教程,Ng的,逻辑清晰有练习):一 ufldl的2个教程(这个没得说,入门绝对的好教程,Ng的,逻辑清晰有练习): ...
- 有关webapplicationcontext的初始化
ApplicationContext是Spring的核心,Context我们通常解释为上下文环境,我想用“容器”来表述它更容易理解一些,ApplicationContext则是“应用的容器”了:在We ...
- 安装、配置、启动FTP、SSH或NFS服务
(1)准备使用软件维护工具apt-get. Ubuntu7.10中没有安装FTP.SSH.NFS服务器软件,它提供了一个很方便的安装.升级.维护软件的工具apt-get.apt-get从光盘.网络上下 ...
- SVN的学习以及使用!
什么是版本控制? 版本控制是记录一个或若干文件内容变化的系统.以便将来查阅特定版本修订情况. 版本控制,就像是一本历史书,记录了软件版本的迭代过程. 为什么需要"版本控制" 需要清 ...