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. GreenDao 数据库:使用Raw文件夹下的数据库文件以及数据库升级

    一.使用Raw文件夹下的数据库文件 在使用GreenDao框架时,数据库和数据表都是根据生成的框架代码来自动创建的,从生成的DaoMaster中的OpenHelper类可以看出: public sta ...

  2. 在 ML2 中配置 OVS flat network - 每天5分钟玩转 OpenStack(133)

    前面讨论了 OVS local network,今天开始学习 flat network. flat network 是不带 tag 的网络,宿主机的物理网卡通过网桥与 flat network 连接, ...

  3. ASP.NET WebApi OWIN 实现 OAuth 2.0

    OAuth(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用. OAuth 允许用户提供一个令牌, ...

  4. android 事件分发机制详解(OnTouchListener,OnClick)

    昨天做东西做到触摸事件冲突,以前也经常碰到事件冲突,想到要研究一下Android的事件冲突机制,于是从昨天开始到今天整整一天时间都要了解这方面的知识,这才懂了安卓的触摸和点击事件的机制.探究如下: 首 ...

  5. Dynamics CRM 之ADFS 使用 WID 的联合服务器场

    使用 WID 的联合服务器场 默认拓扑 Active Directory 联合身份验证服务 (AD FS) 是联合服务器场,使用 Windows 内部数据库 (WID). 在这种拓扑, AD FS 使 ...

  6. 编译器开发系列--Ocelot语言4.类型定义的检查

    这里主要介绍一下检查循环定义的结构体.联合体.是对成员中包含自己本身的结构体.联合体进行检查.所谓"成员中包含自己本身",举例来说,就是指下面这样的定义. struct point ...

  7. React Native Android gradle下载慢问题解决

    很多人会遇到 初次运行 react-native run android的时候 gradle下载极慢,甚至会失败的问题 如下图 实际上这个问题好解决的 首先 把对应版本的gradle下载到本地任意一个 ...

  8. (转载)linux下各个文件夹的作用

    linux下的文件结构,看看每个文件夹都是干吗用的/bin 二进制可执行命令 /dev 设备特殊文件 /etc 系统管理和配置文件 /etc/rc.d 启动的配置文件和脚本 /home 用户主目录的基 ...

  9. [转]ThinkPHP中实例化对象M()和D()的区别,select和find的区别

    1.ThinkPHP中实例化对象M()和D()的区别 在实例化的过程中,经常使用D方法和M方法,这两个方法的区别在于M方法实例化模型无需用户为每个数据表定义模型类,如果D方法没有找到定义的模型类,则会 ...

  10. 使用CocosSharp制作一个游戏 - CocosSharp中文教程

    注:本教程翻译自官方<Walkthrough - Building a game with CocosSharp>,官方教程有很多地方说的不够详细,或者代码不全,导致无法继续,本人在看了G ...