JSPatch - 随笔记录常见问题

接触JSPatch也快一年,在此总结归纳一些新接触的开发者容易碰到的一些问题。其实很多问题bang哥在wiki中已经写的很详细了,建议花点时间仔细看看下面这两个WiKi内容。

想要了解JSPatch实现原理可以先看 JSPatch 实现原理详解

require导入类

只需要一对' ', 多个类之间用逗号 ,分隔:

1
require('UIView, UIColor')

方法遇到下划线_需要特别注意

因为JSPatch是通过_来分割方法参数的,所以遇到方法本身带有_的时候需要使用__双下划线代替,否则会被识别成一个参数,导致出现类似如下的报错。


 报错信息

1
2
3
4
5
6
7
//OC方法代码如下
- (BOOL)_test_Bool {
NSLog(@"我是oc testBool");
return NO;
}
//JS错误调用代码如下
var testBool = self._test_Bool();

我们知道selector 的 一个:代表有一个参数,结合报错信息可知runtime所找的方法是带两个参数的,所以肯定跟你需要调用的方法不一致,找不到则会导致崩溃。
把JS调用代码改为下面这种即可,每个下划线_变成双_,同理如果你方法中本身是双下划线__则需要变成____四个下划线。


1
2
//JS正确调用代码如下
var testBool = self.__test__Bool();

 Safari控制台打印结果

如果需要替换的方法本身带有下划线,与方法调用相同,也同样需要双下划线__

1
2
3
4
__test__Bool:function(){
console.log("我是js  testBool");
return YES;
}

替换成功

undefined is not an object(evaluating ‘self.__c’)

提示意思是self这个对象不存在,结合JSPatch的使用场景可以推测出这个错误一般是由于在block中使用self引起的,解决办法参考block 里使用 self 变量。但值得注意的是,在JS中,如果报了类似的undefined is not an object aaa.bbb错误,则是因为aaa这个对象为undefined,而你又要访问一个undefined的对象属性或者方法在JS中会导致崩溃。

一个最简单高效获取对象类名方法

大多数需求场景下可以通过object.__clsName 的方式获取OC对象类名。不需要通过isKindOfClass或者NSStringFromClass(此方法需要预先添加C函数扩展)来获取,而且不需要与OC相互通信。

1
2
3
4
5
//替换按钮点击方法
handleBtn: function(sender) {
var btnClassName = sender.__clsName;
console.log("btnClassName is : " + btnClassName);
}

 图一:Safati断点查看获取的Button对象
 图二:控制台打印对象类名

下面列举一些有__clsName属性的OC对象情况。

  • JSPatch 通过hook消息转发forwardInvocation: 方法,在调用替换方法或者新添加的方法时给参数添加了__clsName标记来保存类名(所以类似图一的sender参数就会有__clsName属性),但是Class、NSNumber、BOOL、CGFloat、结构体不知道还有没有o(╯□╰)o,感兴趣的可以去查看源码除外。
  • 通过 require 引入的类,所以在JS内通过init出来的对象都会有__clsName属性。
  • 属性和私有变量值和方法返回值(Class、NSNumber、BOOL、CGFloat、结构体…除外,不过这些好像需要知道类名情况比较少)
  • 欢迎补充

OC对象和JS对象

大家在JSPatch交流群里所说的OC对象指的是从OC中获取的JS对象如var str = self.str() 或 通过JS初始化的对象var str = NSString.stringWithFormat("Hello World"),而JS对象指的是例如 var str = "我是js对象"var str = self.str().toJS()之后的JS对象。OC对象应该使用OC中相应方法,JS对象应该使用JS中相应方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//替换按钮点击方法
handleBtn: function(sender) {
var btnClassName = sender.__clsName;
console.log("btnClassName is : " + btnClassName);
//初始化的OC对象
var ocInitStr = NSString.stringWithFormat("Hello World")
//获取属性得到的OC对象
var str = self.str();
console.log("ocInitStr 截取前5个字符:" + ocInitStr.substringToIndex(5).toJS())
//JS对象
var jsStr = "我是JS对象";
console.log("jsStr 截取前4个字符:" + jsStr.substr(0,4))
}


打印结果
per

JS中如何使用强转

答:不需要强转,直接当成强转之后的类型使用,JS里面没有类型的概念,都是对象。