原文地址:https://github.com/bang590/JSPatch/wiki/JSPatch%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98%E8%A7%A3%E7%AD%94
一、block使用规则
1、在 JSPatch 若要向 Objective-C 传递 block,需要使用 block(paramTypes, function)
函数封装:Block的详细使用方法,封装后的值已经不是 JS 函数,不能直接在 JS 上调用:
var blk = block("BOOL", function(b){
})
blk(1) //crash
若想在 JS 上调用,请调用封装前的函数:
var func = function(b){
};
var blk = block("BOOL", func)
func(1) //it's OK
2、不允许在 JS 的 block 中传入含有 undefined
的数组或对象。比如传入 ["JSPatch", undefined]
的数组或 {obj: undefined}
的对象,这种语法在 JavaScript 中没有错误,但是在 Objective-C 中是不允许的。
3、如果 block 的参数里含有 block,paramTypes 需要写成 NSBlock *
。
var blk = block("BOOL, NSBlock *", function(b, blk){
if (b) {
blk(true)
}
})
4、在 Objective-C 中传入到 JSPatch 中的 Block 会转换为 function,如果需要再将该 Block 传回到 OC,依旧需要用 block(paramTypes, function)
封装。
//Objective-C代码
- (void)excuteBlock:(JSBlock)block {
if (block) {
block(@"Hello, JSPatch");
}
}
- (JSBlock)returnBlock {
JSBlock block = ^(NSString *str) {
NSLog(@"%@", str);
};
return block;
}
//JSPatch脚本
var blk = self.returnBlock()
self.excuteBlock(block("NSString *", blk))
5、暂时不支持向OC端传递含有浮点型参数的block,比如如下的情况:
//Objective-C代码
typedef void (^JSBlock)(CGFloat fl); - (void)doBlock:(JSBlock)blk {
if (blk) {
blk(123.456f);
}
}
//JSPatch脚本
self.doBlock(block("CGFloat",function(fl){
console.log(fl) //得到的结果不会是你想要的结果
}
))
6、在block中使用到self时,需要在使用block之前先将self赋值给另外一个变量,然后在block中使用这个变量。如:
//JSPatch脚本
var slf = self
self.requestWithCompletion(block("BOOL, NSError*", function(success, error){
slf.label().setText("complete")
})
)
二、在 JSPatch 中表示 nil
和 [NSNull null]
及使用其他特殊类型
1、在 JSPatch 中使用 null
或 undefined
来表示 Objective-C 的 nil
, 使用 nsnull
表示[NSNull null]
。
2、在 JSPatch 中判断 Objective-C对象是否为空时,使用 if(!obj){}
的形式而不能使用 if(obj == null){}
或 if(obj == undefined){}
3、在 JSPatch 中使用 struct、selector 的用法可参考这里
三、调用包含id *
类型参数的方法
四、在 JSPatch 使用父类方法
使用 self.super()
调用父类方法
defineClass("JPTableViewController", {
viewDidLoad: function() {
self.super().viewDidLoad();
}
})
五、调用多参数的方法
JSPatch 中使用 _
来替代Objective-C中的 :
, 多以调用多参数的 Objective-C 方法需要像下面这样。
var error = require('NSError').errorWithDomain_code_userInfo("UnKnown_Error", -1, null)
如果调用的方法含有一个下划线,就需要使用双下划线来表示。例如:
- (void)p_privateMethod;
JSPatch中可以这样调用
self.p__privateMethod()
不支持方法中含有双(或更多)下划线的方法,如 - (void)p__privateMethod;
。 但是如果是要获取含有双(或更多)下划线的property(这种情况比带有双下划线的方法更为常见一些),可以使用 KVC 的 valueForKey
方法。例如:
@property (nonatomic, string) id __privateObject;
JSPatch中可以这样获取
var obj = self.valueForKey("__privateObject")
六、JSPatch 中的 NSArray,NSString,NSDictionary
在 JSPatch 中不能将 Objective-C 中的 NSArray
, NSString
, NSDictionary
与 JavaScript 的Array
, String
, Object
进行混用。
//Objective-C
- (NSArray *)returnNSArray {
return @[@"Objective-C", @"Swift"];
}
var nsarray = self.returnNSArray()
var array = ["Objective-C", "Swift"]
//这两者不能混用,因为他们是不同的类型。 var wrongStr = nsarray[0] //得到的是一个不正确的对象,请不要使用取下标的方式获取NSArray/NSDictionar对象。 var rightStr = nsarray.toJS()[0] //先unbox返回的对象,然后取出其中的数据
或
var rightStr = nsarray.objectAtIndex(0) //使用Objective-C方法获取NSArray/NSDictionary中的对象
具体可参考基础用法文档
七、常量与宏
Objective-C 里的常量不能直接在 JS 上使用,可以直接在 JS 上用具体值代替,或者在 JS 上重新定义同名的全局变量:
//js
var UIRectEdgeNone = 0,
UIRectEdgeTop = 1 << 0,
UIRectEdgeLeft = 1 << 1,
UIRectEdgeBottom = 1 << 2,
UIRectEdgeRight = 1 << 3
Objective-C 里的宏同样不能直接在 JS 上使用,若定义的宏是一个值,可以在 JS 定义同样的全局变量代替,若定义的宏是程序,或者在底层才能获取到的值,例如 CGFLOAT_MIN
,可以通过添加扩展的方式提供支持:
@implementation JPMacroSupport
+ (void)main:(JSContext *)context
{
context[@"CGFLOAT_MIN"] = ^CGFloat() {
return CGFLOAT_MIN;
}
}
@end
require('JPEngine').addExtensions(['JPMacroSupport'])
var floatMin = CGFLOAT_MIN();
八、Property / 私有变量的获取和赋值
九、类全局变量
目前在类里定义的 static
全局变量无法在 JS 上获取到,若要在 JS 拿到这个变量,需要在 OC 有类方法或实例方法它返回:
static NSString *name;
@implementation JPTestObject
+ (NSString *)name {
return name;
}
@end
var name = JPTestObject.name() //拿到全局变量值
十、Swift
使用 defineClass()
覆盖 Swift 类时,类名应为 项目名.原类名
,例如项目 demo 里用 Swift 定义了 ViewController 类,在 JS 覆盖这个类方法时要这样写:
defineClass('demo.ViewController', {})
十一、可变参数
JSPatch 不支持带可变参数方法的调用,例如:
//OC: [NSString stringWithFormat:@"%d %@", i, obj];
NSString.stringWithFormat("%d %@", i, obj); //not working
因为 JSPatch 原理是通过 NSInvocation 动态调用方法,而 NSInvocation 不支持可变参数,参见官方文档。
十二、其他问题
不可以使用 require('NSNumber').alloc()
,原因见这里