大钟的ios开发之旅(2)————聊聊Runtime 定义

/********************************************************************************************
 * author:conowen@大钟                                                                                                                          
 * E-mail:conowen@hotmail.com                                                                                                             
 * http://blog.csdn.net/conowen                                                                                                              

 ********************************************************************************************/


一、前言

      iOS程序已经有一段时间了,毕业到现在,一直都在创业公司里面奋斗,整天没日没夜地赶项目,增加新功能。然而,能静下心来沉淀技术,提升自我能力的时间并不多,细想一下,博客也很久没更新了,因为太懒,时间也不多,自己在技术这一块走到越来越慢了。对于iOS开发,知道的也只是很浅显的,虽然知道如何实现,但是并不知道原理,也不知道内部如何构架。接下来我将会慢慢地分享一些iOS比较基础、底层的技术,一来加深对iOS技术的认知,而来也能督促自己,永不止步。


二、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语言,在编译的时候,若是你调用此方法,而这个方法并没有实现,那么在编译的时候就会出现errorObjc这个特性让Objc语言具有动态性,也被称为动态语言。

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

例如NSString的 初始化操作:

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

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

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

#import <Foundation/Foundation.h>

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 <objc/message.h>

#import <objc/runtime.h>


- (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库的实现等等。


四、更多关于runtime的小知识请关注下一篇博文








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值