聊聊Runtime 定义

2016-05-27  本文已影响119人  conowen

* author:conowen@大钟

* E-mail:conowen@hotmail.com


一、Runtime简介

首先说说编程语言里面的Run time(运行时)对应的有Compile time(编译时),简单来说,rumtime(运行时)就是把代码装载到内存里面,由CPU运行的时候,这过程同时包括链接各种库,Compile time(编译时),顾名思义就是正在编译的时候.编译器帮你把代码翻译成“机器语言”。一般来说这种“机器语言”是一种中间状态语言,如Java语言中,编译器会把Java源代码翻译为JVM字节码,而Objc语言里面,编译器会把Objc源代码翻译为runtime代码。而这些runtime代码将会被runtime系统执行。Objc 的Runtime其实是一个Runtime库,它是用C和汇编写成的,这个库使得C语言有了面向对象的特性。

以下代码将会展示编译时与运行时的区别。

NSArray *arr0 = @[@"a",@"b"];

NSLog(@"str =%@",[arr0 objectAtIndex:2]);//crash

NSArray *arr1 = [[NSString alloc] initWithString:@"test"];

NSLog(@"count =%lu",arr1.count);//crash

上述Objc代码在编译的时候并没有error,但是运行的时候就会crash。Objc在编译的时候并不会去真正地调用这个方法,只要你声明了这个方法,Objc在运行的时候,才会根据这个方法名,找到对应的方法,然后真正地执行。但是对于C语言,在编译的时候,若是你调用此方法,而这个方法并没有实现,那么在编译的时候就会出现error。Objc这个特性让Objc语言具有动态性,也被称为动态语言。

简单来说,动态语言就是在运行时才会执行静态语言的编译、链接工作,而这些工作,静态语言在编译时就已经完成了。那Objc为嘛要采用这种方式呢?这是因为Objc是由Smalltalk语言演化而来,(Smalltalk语言是消息性语言的始祖,例如a + b这个代码,是“对象a”发送“+”消息,参数是“对象b”)。虽然平常写消息型语言(例如Objc)代码看起来是调用哪个方法,实际编译的时候编译器会把这些“方法”转化为runtime系统通用的“发送消息”方法。

例如NSString的初始化操作:

NSString *str = [NSString stringWithFormat:@"%@",@"conowen"];

实际就是向消息接收者NSString发送了stringWithFormat消息,而消息接收者NSString接收到消息消息的时候,才会查找这个消息对应的方法。这一步实在运行时才执行,编译的时候,编译器并不关心消息接收者NSString是什么类型的对象,或者这个消息对应的方法是否能找得到。

详细过程可以看以下代码,新建一个Objc文件,写入以下代码

#import

int main (int argc, const char * argv[])

{

@autoreleasepool

{

NSString *str = [NSString stringWithFormat:@"%@",@"conowen"];

NSLog(@"length = %lu",[str length]);

}

return 0;

}

在终端输入编译命令:clang -rewrite-objc Test.m

编译成功之后就会在同目录下生成一个同名的cpp文件,这就是runtime代码。打开cpp文件,在最后cpp的末尾可以发现以下runtime代码,就是我们初始化NSString,然后用NSLog方法打印的实现方式。

int main (int argc, const char * argv[])

{

/* @autoreleasepool */

{ __AtAutoreleasePool __autoreleasepool;

NSString *str = ((NSString *(*)(id, SEL, NSString *, ...))(void *)objc_msgSend)((id)objc_getClass("NSString"), sel_registerName("stringWithFormat:"), (NSString *)&__NSConstantStringImpl__var_folders_9t__l4hvdvx6q7cmddt7cdf8xw00000gn_T_Test_084efb_mi_0, (NSString *)&__NSConstantStringImpl__var_folders_9t__l4hvdvx6q7cmddt7cdf8xw00000gn_T_Test_084efb_mi_1);

NSLog((NSString *)&__NSConstantStringImpl__var_folders_9t__l4hvdvx6q7cmddt7cdf8xw00000gn_T_Test_084efb_mi_2,((NSUInteger (*)(id, SEL))(void *)objc_msgSend)((id)str, sel_registerName("length")));

}

return 0;

}

可以发现关键的

objc_msgSend

关于这个是啥东西,下一篇博文再详细解说。因为Objc代码最终都会转成这种Runtime代码,那么我们可以简单地把这部分的Runtime代码直接用起来。

新建一个空白工程,写入如下代码

头文件记得导入如下俩文件

#import

#import

- (void)viewDidLoad {

[super viewDidLoad];

// Do any additional setup after loading the view, typically from a nib.

NSString *str0 = [NSString stringWithFormat:@"%@",@"conowen"];

NSLog(@"length = %lu",[str0 length]);

NSString *str = ((NSString *(*)(id, SEL, NSString *, ...))(void *)objc_msgSend)((id)objc_getClass("NSString"), sel_registerName("stringWithFormat:"),@"%@", @"conowen");

NSLog(@"length = %lu",((NSUInteger (*)(id, SEL))(void *)objc_msgSend)((id)str, sel_registerName("length")));

}

最终 console打印出来的结果如下

2016-05-20 15:00:39.495 Runtime[3066:1128470] length = 7

2016-05-20 15:00:39.496 Runtime[3066:1128470] length = 7

效果是一样的。

二、runtime能干嘛

说了这么多,那Objc的runtime到底能干嘛?看到这里,你应该对runtime有个大概的了解,Objc代码编译后皆转成runtime系统所能认识的编码。也就是说,我可以直接编写runtime代码来实现Objc代码所实现的功能,这样看起来更cool一点,然而并不止是为了cool才去写runtime代码。因为runtime更加底层,我们可以动态地去修改、拦截或者替换系统的一些方法,因为这些都会转成runtime代码。我们在Objc层改不了的话,那就直接在runtime改咯。具体的应用如json转model的第三方库,Category的实现,JSpatch库的实现等等。

上一篇 下一篇

猜你喜欢

热点阅读