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


运行时

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特性的代码,如下是运行时添加方法:

 #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函数等其他高级功能,有兴趣的可以关注下。

由objC运行时所想到的。。。的更多相关文章

  1. ObjC运行时部分概念解析(二)

    上篇文章简单的说明了两个关键字究竟是什么,这里主要讲讲ObjC中各种基本内存模型 Method typedef struct objc_method *Method; struct objc_meth ...

  2. ObjC运行时部分概念解析(一)

    转型iOS已经许久了,Runtime(运行时)还没有好好了解过.之前没有阅读过源码,紧紧凭借自己的臆测.现在阅读下源码,做一些笔记.方便再次翻阅 SEL SEL是一个关键字,如果没有涉及runtime ...

  3. OBJC运行时方法替换(Method swizzling)

    在上周associated objects一文中,我们开始探索Objective-C运行时的一些黑魔法.本周我们继续前行,来讨论可能是最受争议的运行时技术:method swizzling.   Me ...

  4. Objc运行时读取和写入plist文件遇到的问题

    下面是本猫保持游戏NPC和物件交互的plist文件: 随着游戏和玩家逐步发生互动,玩家会修改人物和物件的交互的状态.这也是RPG游戏最基本的功能. 在切换每个地图时需要将上一个地图发生的改变存储到pl ...

  5. Swift运行时简介

    因为Swift的操作在高层并且也得与Objc联合起来干活,用Swift写的程序一般会被Objc和Swift运行时处理.因为Swift的本性--换句话说,它是一门静态语言--Swift运行时在一些关键地 ...

  6. objc语言的运行时处理

    在Objective-C中,消息是通过objc_msgSend()这个runtime方法及相近的方法来实现的.这个方法需要一个target,selector,还有一些参数.理论上来说,编译器只是把消息 ...

  7. Objective-C Runtime 运行时之三:方法与消息

    基础数据类型 SEL SEL又叫选择器,是表示一个方法的selector的指针,其定义如下: typedef struct objc_selector *SEL; objc_selector结构体的详 ...

  8. Runtime运行时机制

    Runtime 又叫运行时,是一套底层的 C 语言 API,其为 iOS 内部的核心之一,我们平时编写的 OC 代码,底层都是基于它来实现的 我们需要了解的是 Objective-C 是一门动态语言, ...

  9. Objective-C Runtime 运行时之三:方法与消息(转载)

    前面我们讨论了Runtime中对类和对象的处理,及对成员变量与属性的处理.这一章,我们就要开始讨论Runtime中最有意思的一部分:消息处理机制.我们将详细讨论消息的发送及消息的转发.不过在讨论消息之 ...

随机推荐

  1. PHP实现RTX发送消息提醒

    RTX是腾讯公司推出的企业级即时通信平台,大多数公司都在使用它,但是我们很多时候需要将自己系统或者产品的一些通知实时推送给RTX,这就需要用到RTX的服务端SDK,建议先去看看RTX的SDK开发文档( ...

  2. MVVM TextBox的键盘事件

    MVVM下RichTextBox的键盘回车事件设置为发送,不是回车 xmlns:i="http://schemas.microsoft.com/expression/2010/interac ...

  3. 算法与数据结构(十一) 平衡二叉树(AVL树)

    今天的博客是在上一篇博客的基础上进行的延伸.上一篇博客我们主要聊了二叉排序树,详情请戳<二叉排序树的查找.插入与删除>.本篇博客我们就在二叉排序树的基础上来聊聊平衡二叉树,也叫AVL树,A ...

  4. Angularjs参考框架地址

    1.Table(Grid)参考地址 https://github.com/samu/angular-table https://github.com/daniel-nagy/md-data-table ...

  5. IT持续集成之质量管理

    研发工具生态 质量相关工作 一次编译产出测试包与上线包 !从源头保证版本的⼀一致性!代码质量控制! 全⽅方位的⾃自动化测试体系保证! 提测冒烟效率! 全⾃自动上线流程杜绝⼈人⼯工犯错! 生产环境应⽤用 ...

  6. 敏捷转型历程 - Sprint3 一团糟的演示会

    我: Tech Leader 团队:团队成员分布在两个城市,我所在的城市包括我有4个成员,另外一个城市包括SM有7个成员.另外由于我们的BA离职了,我暂代IT 的PO 职位.PM和我在一个城市,但他不 ...

  7. 如何编译Zookeeper源码

    1. 安装Ant Ant下载地址:http://ant.apache.org/bindownload.cgi 解压即可. 2. 下载Zookeeper源码包 https://github.com/ap ...

  8. 怎样两个月完成Udacity Data Analyst Nanodegree

    在迷恋数据科学很久后,我决定要在MOOC网站上拿到一份Data Science的证书.美国三个MOOC网站,Udacity上的课程已经被分成了数个nanodegree,每个nanodegree都是目前 ...

  9. linux下mono播放PCM音频

         测试环境: Ubuntu 14 MonoDevelop CodeBlocks 1.建立一个共享库(shared library) 这里用到了linux下的音频播放库,alsa-lib. al ...

  10. ES6 箭头函数中的 this?你可能想多了(翻译)

    箭头函数=>无疑是ES6中最受关注的一个新特性了,通过它可以简写 function 函数表达式,你也可以在各种提及箭头函数的地方看到这样的观点——“=> 就是一个新的 function”. ...