前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >由objC运行时所想到的。。。

由objC运行时所想到的。。。

作者头像
欲休
发布2018-03-15 14:19:43
7690
发布2018-03-15 14:19:43
举报
文章被收录于专栏:前端杂货铺前端杂货铺

objC语言不仅仅有着面向对象的特点(封装,继承和多态),也拥有类似脚本语言的灵活(运行时),这让objC有着很多奇特的功能-可在运行时添加给类或对象添加方法,甚至可以添加类方法,甚至可以动态创建类。。。


[TOC]

运行时

runtime,即运行时,这里不详述其概念,我们可以类比java和javascript语言,它们也都有运行时环境。java运行时是和编译阶段相独立的过程,可以理解java字节码在虚拟机中解释执行的阶段的由虚拟机提供的相关引用(gc,存储等)的环境;而js运行时则更为特殊,由于js是一门解释性语言(至少在浏览器端的js是这样的,不考虑v8的全代码编译),传统的js执行并没有解析步骤,而是由js引擎解释执行,因此运行时可以理解为在就是js引擎,亦或者是js引擎执行阶段。

而objC运行时并不像java那样仅仅只能获取运行时的类和构造方法,还可以像js那般随便修改对象甚至删除对象;这样,我们可以通过objC运行时获得脚本语言的特性,来完成很多“未知”的工作。

objC运行时依赖objc/runtime库,通过runtime库,我们可以给类将c语言函数添加为实例方法,同理也可以修改类方法。runtime库通过在底层封装了c层面上的结构体和函数来为objC提供运行时创建,修改,删除的能力。

注:除了封装,objC runtime库也负责找出方法的最终执行代码。当程序执行[object doSomething]时,不会直接找到方法并调用。相反,一条消息(message)会发送给对象。runtime库给次机会让对象根据消息决定该作出什么样的反应。

实践

由于runtime库采用c语言编写,因此使用c语法编写具有runtime特性的代码,如下是运行时添加方法:

代码语言:javascript
复制
 #import "MyTt.h"
 #import <objc/runtime.h>
 @implementation MyTt
 void say(id self, SEL _cmd){

    NSLog(@"Class is %@, super class is %@", [self class], [self superclass]);
    NSLog(@"this is a replace function");
\}

 \- (void)ex_addMethod {
    
    Class newClass = [MyTt class];
    class_addMethod(newClass, @selector(say), (IMP)say, "v@:");
    //class_replaceMethod(newClass, @selector(say), (IMP)say, "v@:");
    
    id instance = [[newClass alloc] init];
    [instance performSelector:@selector(say)];
    
    MyTt *m = [[MyTt alloc] init];
    [m say];
 
}
 @end

输出如下:


2016-01-15 15:53:43.477 oc_runtime[15821:1717275] Class is MyTt, super class is NSObject
2016-01-15 15:53:43.478 oc_runtime[15821:1717275] this is a replace function
2016-01-15 15:53:43.478 oc_runtime[15821:1717275] Class is MyTt, super class is NSObject
2016-01-15 15:53:43.478 oc_runtime[15821:1717275] this is a replace function

可以看出MyTt类中并未定义say方法,而在ex_addMethod中通过运行时添加了c函数作为MyTt的一个实例方法,此后创建的该类实例则拥有改实例方法。

不过需要注意的是class_addMethod方法在类有对应方法时会无效,此时可以通过class_replaceMethod来替换对应方法。

既然objC的runtime这么给力,那么我们可以有一个设想,就是通过objC的runtime完成一些额外的功能实现或bug修复,而且这种功能实现或bug修复的实现代码并不仅限于objC语言,可以使用动态脚本语言完成objC层的逻辑代码,并在objC层进行代码翻译,实现具体逻辑,完成代码动态修补,这样我们可以不用等待漫长的app审核完成bug的热修复。

而如今大多数的iOS设备的app开发采用hybrid模式实现,在UIWebView层上运行的是js业务代码,而js则恰恰也是动态语言,可以随意在运行期间修改对象方法或者原型链,方便实现一些比较有特色的功能(如执行对象并不存在的方法是,可以通过修改原型链或者方法重写实现功能),最重要的是js代码的版本更新迭代十分快捷,如通过objC引用线上js文件,就可以通过修改线上的js代码来实现objC层热修复。

当然,想法是好的,实现过程中可能不会这么顺畅。

畅想

为了实现js代码可以在objC层可以被正确解析并执行,大前提是需要在app中嵌入js引擎,完成js的解释(编译)执行,这在iOS7及以上版本可以通过引入javascriptCore框架实现,通过构建一个JSMachine存储JSContext的各种引用并管理其生命周期,提供js代码的执行环境;

其次需要完成objC层的基本类,继承链和协议与js层的对象(js没有class的概念)一一对应,并且保证objC的类的方法在js层的同样可以获取到,这就涉及到js对象调用objC层方法的一些处理,可以通过继承响应对象的原型链实现该功能,对于objC层的属性可在js层通过get/set方法实现。

另外需要处理的则是js方法名到objC方法名的转换,由于objC方法是多参数类型的,因此针对“:”分隔符需要在js层做相应的转换处理。

最后,要实现一个通用方法,将js层函数的参数一一对应到objC层的方法上,因此参数的传递也是一大难点。如果单独针对一个函数做实现,可以通过上节的例子一样,给c函数添加第三个参数,这个第三个参数就是传入的参数。但是这并没有提供一种通用的解决方案,好在bang590提供了一种解决方案,即通过objC语言特有的消息传递机制实现的一种hack(其实objC的方法调用本身就是一种消息机制,如obj.abc()通常称为向obj对象发送abc消息)。

以上就是针对objC运行时的强大动态功能所想到的一些东西,不过已经有牛人已经实现了上述想法,他就是上文提到的bang590,他通过更为巧妙的构思在js层做到了与objC对象的映射(通过在javascriptcore中正则修改js的相关方法,完成js层的方法重定向),并完成了js层函数参数的传递(通过手动修改objC全局方法forwardInvocation实现消息转发)。当然这只是起jsPatch的一部分功能,还有诸如协议,c函数等其他高级功能,有兴趣的可以关注下。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 运行时
  • 实践
  • 畅想
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档