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. Cmder--Windows下命令行利器

    cmder cmder是一个增强型命令行工具,不仅可以使用windows下的所有命令,更爽的是可以使用linux的命令,shell命令. 安装包 安装包链接 下载后,直接解压即用. 修改命令提示符λ为 ...

  2. MVVM框架从WPF移植到UWP遇到的问题和解决方法

    MVVM框架从WPF移植到UWP遇到的问题和解决方法 0x00 起因 这几天开始学习UWP了,之前有WPF经验,所以总体感觉还可以,看了一些基础概念和主题,写了几个测试程序,突然想起来了前一段时间在W ...

  3. JSON.parse()和JSON.stringify()

    1.parse 用于从一个字符串中解析出json 对象.例如 var str='{"name":"cpf","age":"23&q ...

  4. 0.Win8.1,Win10,Windows Server 2012 安装 Net Framework 3.5

    后期会在博客首发更新:http://dnt.dkill.net 网站部署之~Windows Server | 本地部署:http://www.cnblogs.com/dunitian/p/482280 ...

  5. Android和JavaScript相互调用的方法

    转载地址:http://www.jb51.net/article/77206.htm 这篇文章主要介绍了Android和JavaScript相互调用的方法,实例分析了Android的WebView执行 ...

  6. .NET平台开源项目速览(13)机器学习组件Accord.NET框架功能介绍

    Accord.NET Framework是在AForge.NET项目的基础上封装和进一步开发而来.因为AForge.NET更注重与一些底层和广度,而Accord.NET Framework更注重与机器 ...

  7. XSS分析及预防

    XSS(Cross Site Scripting),又称跨站脚本,XSS的重点不在于跨站点,而是在于脚本的执行.在WEB前端应用日益发展的今天,XSS漏洞尤其容易被开发人员忽视,最终可能造成对个人信息 ...

  8. FullCalendar日历插件说明文档

    FullCalendar提供了丰富的属性设置和方法调用,开发者可以根据FullCalendar提供的API快速完成一个日历日程的开发,本文将FullCalendar的常用属性和方法.回调函数等整理成中 ...

  9. Android之数据存储的五种方法

    1.Android数据存储的五种方法 (1)SharedPreferences数据存储 详情介绍:http://www.cnblogs.com/zhangmiao14/p/6201900.html 优 ...

  10. Android 微信第三方登录(个人笔记)

    今天在写微信登录,花了半天时间搞定.然后写下自己的笔记,希望帮助更多的人...欢迎各位指教. 微信授权登录,官方说的不是很清楚.所以导致有一部分的坑. 微信注册应用平台的应用签名,下载 微信签名生成工 ...