Base usage

bang edited this page Dec 25, 2015 · 17 revisions

1. require

Call require('className') before using the Objective-C class.

require('UIView')
var view = UIView.alloc().init()

You can use , to separate multiple class to import them at one time.

require('UIView, UIColor')
var view = UIView.alloc().init()
var red = UIColor.redColor()

You can use require() directly:

require('UIView').alloc().init()

2. Invoking method

Invoking class method:

var redColor = UIColor.redColor();

Invoking instance method:

var view = UIView.alloc().init();
view.setNeedsLayout();

Pass params as you do in Obj-C:

var view = UIView.alloc().init();
var superView = UIView.alloc().init()
superView.addSubview(view)

Getting/Setting properties:

view.setBackgroundColor(redColor);
var bgColor = view.backgroundColor();

Use _ to seperate multi-params:

var indexPath = require('NSIndexPath').indexPathForRow_inSection(0, 1);

Use __ to represent _ in the original Obj-C method name:

// Obj-C: [JPObject _privateMethod];
JPObject.__privateMethod()

3. Special types

Use hash object to represent CGRect / CGPoint / CGSize / NSRange

// 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}

Use String to represent Selector in JS:

//Obj-C
[self performSelector:@selector(viewWillAppear:) withObject:@(YES)];
//JS
self.performSelector_withObject("viewWillAppear:", 1)

null and undefined will pass as nil in Objective-C, use nsnull to represent 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

4. NSArray / NSString / NSDictionary

Use NSArray / NSString / NSDictionary as normal NSObject :

//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

use .toJS() to convert NSArray / NSString / NSDictionary to JS type.

// 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

5. Block

Use block(paramTypes, function) to warp JS function when passing JS function as block to Obj-C

// 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
}))

Just call directly when the block passing from Obj-C to JS:

// 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;
}
@end
// JS
var blk = require('JPObject').genBlock();
blk({v: "0.0.1"});  //output: I'm JSPatch, version: 0.0.1

The self in defineClass() can't use in the async block, you should catch it if you want to use it in the block:

defineClass("JPViewController", {
  viewDidLoad: function() {
    var slf = self;
    require("JPTestObject").callBlock(block(function(){
      //`self` is not available here, use `slf` instead.
      slf.doSomething();
    });
  }
}

There is 2 limitation when passing block from JS to Obj-C:

A. Only supports up to 4 params. (you can modify the source code if you want more)
B. Params types can not be double.

6. __weak / __strong

Use __weak() in JS to declare a weak variable, usually use to avoid retain circle.

For example, to avoid retain circle in block, we usually writing like this:

- (void)test {
    __weak id weakSelf = self;
    [self setCompleteBlock:^(){
        [self blabla];
    }]
}

We can translate to JS like this:

var weakSelf = __weak(self)
self.setCompleteBlock(block(function(){
    weakSelf.blabla();
}))

If we want to strongify the weak variable before using it, use __strong():

var weakSelf = __weak(self)
self.setCompleteBlock(block(function(){
    var storngSelf = __strong(weakSelf)
    strongSelf.blabla();
}))

7. GCD

Use dispatch_after() dispatch_async_main() dispatch_sync_main() dispatch_async_global_queue() to call 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
})

8. Passing id* parameters

If you are calling a method which have id* parameters like NSError **, you can pass a pointer to it, follow these steps:

  1. use malloc(sizeof(id)) to create a pointer.
  2. pass pointer to the method.
  3. use pval() to get the object.
  4. use releaseTmpObj() to release object after used.
  5. use free() to release pointer.

For example:

//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)