由objC运行时所想到的。。。
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运行时所想到的。。。的更多相关文章
- ObjC运行时部分概念解析(二)
上篇文章简单的说明了两个关键字究竟是什么,这里主要讲讲ObjC中各种基本内存模型 Method typedef struct objc_method *Method; struct objc_meth ...
- ObjC运行时部分概念解析(一)
转型iOS已经许久了,Runtime(运行时)还没有好好了解过.之前没有阅读过源码,紧紧凭借自己的臆测.现在阅读下源码,做一些笔记.方便再次翻阅 SEL SEL是一个关键字,如果没有涉及runtime ...
- OBJC运行时方法替换(Method swizzling)
在上周associated objects一文中,我们开始探索Objective-C运行时的一些黑魔法.本周我们继续前行,来讨论可能是最受争议的运行时技术:method swizzling. Me ...
- Objc运行时读取和写入plist文件遇到的问题
下面是本猫保持游戏NPC和物件交互的plist文件: 随着游戏和玩家逐步发生互动,玩家会修改人物和物件的交互的状态.这也是RPG游戏最基本的功能. 在切换每个地图时需要将上一个地图发生的改变存储到pl ...
- Swift运行时简介
因为Swift的操作在高层并且也得与Objc联合起来干活,用Swift写的程序一般会被Objc和Swift运行时处理.因为Swift的本性--换句话说,它是一门静态语言--Swift运行时在一些关键地 ...
- objc语言的运行时处理
在Objective-C中,消息是通过objc_msgSend()这个runtime方法及相近的方法来实现的.这个方法需要一个target,selector,还有一些参数.理论上来说,编译器只是把消息 ...
- Objective-C Runtime 运行时之三:方法与消息
基础数据类型 SEL SEL又叫选择器,是表示一个方法的selector的指针,其定义如下: typedef struct objc_selector *SEL; objc_selector结构体的详 ...
- Runtime运行时机制
Runtime 又叫运行时,是一套底层的 C 语言 API,其为 iOS 内部的核心之一,我们平时编写的 OC 代码,底层都是基于它来实现的 我们需要了解的是 Objective-C 是一门动态语言, ...
- Objective-C Runtime 运行时之三:方法与消息(转载)
前面我们讨论了Runtime中对类和对象的处理,及对成员变量与属性的处理.这一章,我们就要开始讨论Runtime中最有意思的一部分:消息处理机制.我们将详细讨论消息的发送及消息的转发.不过在讨论消息之 ...
随机推荐
- C语言 · 高精度加法
问题描述 输入两个整数a和b,输出这两个整数的和.a和b都不超过100位. 算法描述 由于a和b都比较大,所以不能直接使用语言中的标准数据类型来存储.对于这种问题,一般使用数组来处理. 定义一个数组A ...
- 戏说HTML5
如果有非技术人员问你,HTML5是什么,你会怎么回答? 新的HTML规范... 给浏览器提供了牛逼能力,干以前不能干的事...(确切地说应该是给浏览器规定了许多新的接口标准,要求浏览器实现牛逼的功能. ...
- nodejs进阶(2)—函数模块调用
函数调用 1. 文件内普通函数调用 创建一个js文件命名为2_callFunction.js,其中定义一个函数fun1,向返回对象输出了一段字符串“你好,我是fun1”. //------------ ...
- nginx源码分析之模块初始化
在nginx启动过程中,模块的初始化是整个启动过程中的重要部分,而且了解了模块初始化的过程对应后面具体分析各个模块会有事半功倍的效果.在我看来,分析源码来了解模块的初始化是最直接不过的了,所以下面主要 ...
- H5项目开发分享——用Canvas合成文字
以前曾用Canvas合成.裁剪.图片等<用H5中的Canvas等技术制作海报>.这次用Canvas来画文字. 下图中"老王考到驾照后"这几个字是画在Canvas上的,与 ...
- Asp.net Core 初探(发布和部署Linux)
前言 俗话说三天不学习,赶不上刘少奇.Asp.net Core更新这么长时间一直观望,周末帝都小雨,宅在家看了下Core Web App,顺便搭建了个HelloWorld环境来尝尝鲜,第一次看到.Ne ...
- 【算法】(查找你附近的人) GeoHash核心原理解析及代码实现
本文地址 原文地址 分享提纲: 0. 引子 1. 感性认识GeoHash 2. GeoHash算法的步骤 3. GeoHash Base32编码长度与精度 4. GeoHash算法 5. 使用注意点( ...
- springmvc+mybatis+spring 整合 bootstrap html5
A 调用摄像头拍照,自定义裁剪编辑头像 [新录针对本系统的视频教程,手把手教开发一个模块,快速掌握本系统]B 集成代码生成器 [正反双向](单表.主表.明细表.树形表,开发利器)+快速构建表单; 技 ...
- Flexible 弹性盒子模型之CSS flex-grow 属性
实例 让第二个元素的宽度为其他元素的三倍: div:nth-of-type(1){flex-grow:1;} div:nth-of-type(2){flex-grow:3;} div:nth-of-t ...
- 【SAP业务模式】之ICS(七):IDOC配置
这是ICS业务模式系列的最后一篇了,主要讲解IDOC的配置. 一.指定EDI传输的供应商逻辑地址 事务代码:WEL1 注意:上面逻辑地址是生产公司+内部客户.有以下两种情形: 1.如果内部客户都是纯数 ...