require('className’)
:
require('UIView') var view = UIView.alloc().init()可以用逗号
,
分隔,一次性导入多个类:
require('UIView, UIColor') var view = UIView.alloc().init() var red = UIColor.redColor()或者直接在使用时才调用
require()
:
require('UIView').alloc().init()
var redColor = UIColor.redColor();
var view = UIView.alloc().init(); view.setNeedsLayout();
跟在OC一样传递参数:
var view = UIView.alloc().init();
var superView = UIView.alloc().init()
superView.addSubview(view)
获取/修改 Property 等于调用这个 Property 的 getter / setter 方法,获取时记得加 ()
:
view.setBackgroundColor(redColor); var bgColor = view.backgroundColor();
多参数方法名使用 _
分隔:
var indexPath = require('NSIndexPath').indexPathForRow_inSection(0, 1);若原 OC 方法名里包含下划线
_
,在 JS 使用双下划线
__
代替:
// Obj-C: [JPObject _privateMethod]; JPObject.__privateMethod()
JSPatch原生支持 CGRect / CGPoint / CGSize / NSRange 这四个 struct 类型,用 JS 对象表示:
// Obj-C UIView *view = [[UIView alloc] initWithFrame:CGRectMake(20, 20, 100, 100)]; [view setCenter:CGPointMake(10,10)]; [view sizeThatFits:CGSizeMake(100, 100)]; CGFloat x = view.frame.origin.x; NSRange range = NSMakeRange(0, 1);
// JS var view = UIView.alloc().initWithFrame({x:20, y:20, width:100, height:100}) view.setCenter({x: 10, y: 10}) view.sizeThatFits({width: 100, height:100}) var x = view.frame().x var range = {location: 0, length: 1}其他 Struct 类型的支持请参照 添加 struct 类型支持
在JS使用字符串代表 Selector:
//Obj-C [self performSelector:@selector(viewWillAppear:) withObject:@(YES)];
//JS self.performSelector_withObject("viewWillAppear:", 1)
nil
JS 上的 null
和 undefined
都代表 OC 的 nil
,如果要表示 NSNull
, 用 nsnull
代替:
//Obj-C @implemention JPTestObject + (BOOL)testNull(NSNull *null) { return [null isKindOfClass:[NSNull class]] } @end
//JS require('JPTestObject').testNull(nsnull) //return 1 require('JPTestObject').testNull(null) //return 0
//Obj-C @implementation JPObject + (NSArray *)data { return @[[NSMutableString stringWithString:@"JS"]] } + (NSMutableDictionary *)dict { return [[NSMutableDictionary alloc] init]; } @end
// JS require('JPObject') var ocStr = JPObject.data().objectAtIndex(0) ocStr.appendString("Patch") var dict = JPObject.dict() dict.setObject_forKey(ocStr, 'name') console.log(dict.objectForKey('name')) //output: JSPatch
如果要把 NSArray / NSString / NSDictionary 转为对应的 JS 类型,使用 .toJS()
接口:
// JS var data = require('JPObject').data().toJS() //data instanceof Array === true data.push("Patch") var dict = JPObject.dict() dict.setObject_forKey(data.join(''), 'name') dict = dict.toJS() console.log(dict['name']) //output: JSPatch
当要把 JS 函数作为 block 参数给 OC时,需要先使用 block(paramTypes, function)
接口包装:
// Obj-C @implementation JPObject + (void)request:(void(^)(NSString *content, BOOL success))callback { callback(@"I'm content", YES); } @end
// JS require('JPObject').request(block("NSString *, BOOL", function(ctn, succ) { if (succ) log(ctn) //output: I'm content }))这里 block 里的参数类型用字符串表示,写上这个 block 各个参数的类型,用逗号分隔。NSObject 对象如
NSString *
,
NSArray *
等可以用
id
表示,但 block 对象要用
NSBlock*
表示。
从 OC 返回给 JS 的 block 会自动转为 JS function,直接调用即可:
// Obj-C @implementation JPObject typedef void (^JSBlock)(NSDictionary *dict); + (JSBlock)genBlock { NSString *ctn = @"JSPatch"; JSBlock block = ^(NSDictionary *dict) { NSLog(@"I'm %@, version: %@", ctn, dict[@"v"]) }; return block; } + (void)execBlock:(JSBlock)blk { } @end
// JS var blk = require('JPObject').genBlock(); blk({v: "0.0.1"}); //output: I'm JSPatch, version: 0.0.1若要把这个从 OC 传过来的 block 再传回给 OC,同样需要再用
block()
包装,因为这里
blk
已经是一个普通的 JS function,跟我们上面定义的 JS function 没有区别:
// JS var blk = require('JPObject').genBlock(); blk({v: "0.0.1"}); //output: I'm JSPatch, version: 0.0.1 require('JPObject').execBlock(block("id", blk));总结:JS 没有 block 类型的变量,OC 的 block 对象传到 JS 会变成 JS function,所有要从 JS 传 block 给 OC 都需要用
block()
接口包装。
在 block 里无法使用 self
变量,需要在进入 block 之前使用临时变量保存它:
defineClass("JPViewController", { viewDidLoad: function() { var slf = self; require("JPTestObject").callBlock(block(function(){ //`self` is not available here, use `slf` instead. slf.doSomething(); }); } }
从 JS 传 block 到 OC,有两个限制:
A. block 参数个数最多支持6个。(若需要支持更多,可以修改源码)
B. block 参数类型不能是 double
。
- (void)callBlock:(void(^)(NSString *str))block { }
defineClass('JPTestObject', { run: function() { self.callBlock(block('NSString*', function(str) { console.log(str); })); }, callBlock: function(blk) { //blk 这个 block 是上面的 run 函数里 JS 传到 OC 再传过来的,无法调用。 blk("test block"); } });
可以在 JS 通过 __weak()
声明一个 weak 变量,主要用于避免循环引用。
例如我们在 OC 里为了避免 block 导致的循环引用,经常这样写:
- (void)test { __weak id weakSelf = self; [self setCompleteBlock:^(){ [weakSelf blabla]; }] }在 JS 对应的可以这样写:
var weakSelf = __weak(self) self.setCompleteBlock(block(function(){ weakSelf.blabla(); }))若要在使用 weakSelf 时把它变成 strong 变量,可以用
__strong()
接口:
var weakSelf = __weak(self) self.setCompleteBlock(block(function(){ var storngSelf = __strong(weakSelf) strongSelf.blabla(); }))
dispatch_after()
dispatch_async_main()
dispatch_sync_main()
dispatch_async_global_queue()
接口调用GCD方法:
// Obj-C dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ // do something }); dispatch_async(dispatch_get_main_queue(), ^{ // do something });
// JS dispatch_after(1.0, function(){ // do something }) dispatch_async_main(function(){ // do something }) dispatch_sync_main(function(){ // do something }) dispatch_async_global_queue(function(){ // do something })
id*
参数id*
参数,像
NSURLConnection
里的这个接口里的
NSError **
:
+ (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response
error:(NSError **)error;
这里传入的是一个指向 NSObject 对象的指针,在方法里可以修改这个指针指向的对象,调用后外部可以拿到新指向的对象,对于这样的参数,需要按以下步骤进行传递和获取:
malloc(sizeof(id))
创建一个指针pval()
拿到指针新指向的对象releaseTmpObj()
释放这个对象free()
释放指针举个例子:
//OC - (void)testPointer:(NSError **)error { NSError *err = [[NSError alloc]initWithDomain:@"com.jspatch" code:42 userInfo:nil]; *error = err; }
//JS //malloc() pval() free() is provided by JPMemory extension require('JPEngine').addExtensions(['JPMemory']) var pError = malloc(sizeof("id")) self.testPointer(pError) var error = pval(pError) if (!error) { console.log("success") } else { console.log(error) } releaseTmpObj(pError) free(pError)
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 定义同样的全局变量代替,若定义的宏是程序,可以在JS展开宏:
#define TABBAR_HEIGHT 40 #define SCREEN_WIDTH [[UIScreen mainScreen] bounds].size.height
//JS //instead of var tabbarHeight = TABBAR_HEIGHT var tabbarHeight = 40; //instead of var width = SCREEN_WIDTH var width = UIScreen.mainScreen().bounds().height;若宏的值是某些在底层才能获取到的值,例如
CGFLOAT_MIN
,可以通过在某个类或实例方法里将它返回,或者用添加扩展的方式提供支持:
@implementation JPMacroSupport + (void)main:(JSContext *)context { context[@"CGFLOAT_MIN"] = ^CGFloat() { return CGFLOAT_MIN; } } @end
require('JPEngine').addExtensions(['JPMacroSupport']) var floatMin = CGFLOAT_MIN();
在类里定义的 static
全局变量无法在 JS 上获取到,若要在 JS 拿到这个变量,需要在 OC 有类方法或实例方法把它返回:
static NSString *name; @implementation JPTestObject + (NSString *)name { return name; } @end
var name = JPTestObject.name() //拿到全局变量值
defineClass()
覆盖 Swift 类时,类名应为
项目名.原类名
,例如项目 demo 里用 Swift 定义了 ViewController 类,在 JS 覆盖这个类方法时要这样写:
defineClass('demo.ViewController', {})
另外注意:Swift 类需要继承
NSObject
才能被调用和替换,若不是继承 NSObject,需要把要类/属性/方法等声明前面加上
@objc
修饰符,详见
这里
。