JSPatch 踩坑日记

写给蠢蠢的自己

一定要记得时刻加括号括号括号()()()啊小伙伴们 一些小细节坑了我一下午



1. 私有成员变量
============举个栗子==============

// OC
@implementation JPTestViewController { 
       BOOL _hasRead;
}
@end
// JS
defineClass("JPTableViewController", { 
      viewDidLoad: function() {
          var hasRead = self.valueForKey("_hasRead");         //获取成员变量
          self.setValue_forKey(false, "_hasRead");            //设置成员变量
      },
});

2. 特殊类型
JSPatch原生支持 CGRect / CGPoint / CGSize / NSRange 这四个 struct 类型,用 JS 对象表示:

// OC
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(20, 20, 100, 100)];
[view setCenter:CGPointMake(10,10)];
CGSize size = CGSizeMake(100, 100);
CGFloat height = size.height;
[view sizeThatFits:size];
CGFloat x = view.frame.origin.x;
CGFloat width = view.frame.size.width;
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});
var size = {width: 100, height:100};
var height = size.height;
view.sizeThatFits(size);
var x = view.frame().x;
var width = view.frame().width;
var range = {location: 0, length: 1};

3. 字符串拼接

  • 方法一 stringWithFormat
    JSPatch 支持调用 stringWithFormat,不过所有参数类型都需改为 %@:
//OC
[NSString stringWithFormat:@"name:%@, age:%d", @"alex", 12];
//JS
NSString.stringWithFormat("name:%@, age:%@", "alex", 12);
  • 方法二 JS语法拼接
    虽然支持 stringWithFormat,但还是建议使用 JS 语法拼接字符串:
    <u>(此处有坑 切记要将字符串转为JS对象才能操作这些类型,详情请见下面的字符串 / 数组 / 字典 操作问题)</u>
//JS
var ret = "name:" + "alex" + " age:(" + 12 + ")"; 
//注:JS中将int类型转为字符串方法为: 
var age = 12;
var ageStr = 12+"";

4. NSNumber 相关问题
NSNumber 与上述四个类型不一样,所有数值类型以及 NSNumber 对象到 JS 后都会变成数值,不能再调用这个数值的任何方法:
翻译过来就是 JS里不存在NSNumber这玩意儿!只能当数值用 所有跟NSNumber有关的方法例如isEqualToNumber或者stringValue等都不能用。

5. 字符串 / 数组 / 字典 操作问题
刚使用 JSPatch 经常会对 NSString / NSArray / NSDictionary / NSDate 这四个类的使用感到迷惑,因为 JS 语言本身有对应的这四个类型,会跟 OC 的这四个类混淆。要避免混淆,要弄清楚两点:

  1. 需要认清这四个类有 JS 跟 OC 两种类型
//OC
@implementation JPTestObject
+(NSString *)name{
     return @"I'm NSString";
}
+(NSMutableDictionary *)info{
    return @{@"k": @"v"};
}
+(NSArray *)users{ 
    return @[@"alex", @"bang", @"cat"];
}
@end
//JS
var ocStr = JPTestObject.name();
var ocInfo = JPTestObject.info();
var ocUsers = JPTestObject.users();
//以上三个是从 OC 返回的 OC 对象,可以调用 OC 方法:
ocStr.rangeOfString("I'm"); //OK
ocInfo.addObject_forKey("a", "b"); //OK
ocUsers.firstObject(); //OK
///////////////////////////////////////
var str = "I'm JS String";
var info = @{"k": "v"};
var users = ["alex", "bang", "cat"];
//以上三个是 JS 对象,不能调用 OC 方法:
str.rangeOfString("I'm"); //crash
info.addObject_forKey("a", "b"); //crash
users.firstObject(); //crash
@end
  1. 若要用JS语法操作这些类型,要确保它是 JS 对象
//JS
//错误:ocStr 不是 JS 对象,不能用 JS 语法拼接字符串
var newStr = ocStr + "js string"; 
//正确:已用 .toJS() 接口转为 JS 对象,可以用 JS语法操作
var transStr = ocStr.toJS();
var newStr = transStr + "js string";
 //错误:ocUsers 不是 JS 对象,不能用[]语法,也不能用 JS 语法遍历
var firstUser = ocUser[0];
for (var i = 0; i < ocUsers.length; i ++) {    
        var user = ocUsers[i];
}
//正确:已用 .toJS() 接口转为 JS 对象,可以用 JS语法操作
var transArr = ocUsers.toJS();
var firstUser = transArr[0];
for (var i = 0; i < transArr.length; i ++) {       //注:JS中数组长度为.length不是.count
        var user = transArr[i];
}
//错误: ocInfo 不是 JS 对象,不能用[]语法
var v = ocInfo['k'];
//正确:已用 .toJS() 接口转为 JS 对象,可以用 JS语法操作
var transDict = ocInfo.toJS();
var v = transDict['k'];

============举个栗子==============

//JS
textField_shouldChangeCharactersInRange_replacementString: function(textField, range, string) {
        var str = "string: " + string;    //❌错误写法 传进来的string是OC对象 不能使用JS语法拼接
        var str = "string: " + string.toJS();   //✅正确  先将string转成JS对象 进行拼接
        return true;
},

既然讲到了NSArray,这里顺便讲讲数组遍历for...in
首先从 OC 返回的 NSArray / NSDictionary 对象是不能直接用 for...in 遍历的,需要调用 .toJS() 后才能进行遍历,详情见上文。
然后在遍历数组时,JavaScript 的 for...in 语法定义与 Objective-C 不同:

//OC
NSArray *arr = @[@"name", @"age"];
for (var o in arr) { 
        NSLog(@"%@", o); //输出 name age
}
//JS
var arr = ["name", "age"];
for (var o in arr) { 
      console.log(o); //输出 0, 1,表示遍历数组的序号 
      console.log(arr[o]); //输出 name age,这样才表示数组的值
}

============再来个栗子==============

    tableView_cellForRowAtIndexPath: function(tableView, indexPath)
    {
        var identifier = "Cell";
        var cell = tableView.dequeueReusableCellWithIdentifier(identifier);
        if (!cell) {
            cell = UITableViewCell.alloc().initWithStyle_reuseIdentifier(0, identifier);
        }
        //错误一
        for (var a in cell.contentView().subviews()) {    
            a.removeFromSuperview();
        }
        //错误原因:需要调用.JS()才能使用for...in方法遍历 如用OC对象 则可以使用以下方法遍历
        for (int i = 0; index < cell.contentView().subviews().count(); i++) { 
            var subView = cell.contentView().subviews().objectAtIndex(i);
            subView.removeFromSuperview();
        }
        //或者可以使用以下方法 讲OC对象转成JS对象进行for...in遍历
        //此处有坑 错误二
        var subViews = cell.contentView().subviews().toJS();
        for (var a in subViews) {
            var.removeFromSuperview();    //❌错误 JS中输入的var表示遍历数组的序号 会crash
        }
        //正确姿势
        for (var a in subViews) {
            subViews[a].removeFromSuperview();
        }
        cell.setBackgroundColor(UIColor.whiteColor());
        cell.textLabel().setText("");
        return cell;
    },

6. Block
当要把 JS 函数作为 block 参数给 OC时,需要先使用 block(paramTypes, function) 接口包装:

//OC
@implementation JPObject
+ (void)request:(void(^)(NSString *content, BOOL success))callback{   
     if (success){
          NSLog(@"%@",content);    
          [self doSomething];
     }
}
@end
//JS
//在 block 里无法使用 self 变量,需要在进入 block 之前使用临时变量保存它
var slf = self;
//weak变量跟strong变量的申明
var weakSelf = __weak(self);
require('JPObject').request(block("NSString *, BOOL", function(content, success) { 
      if (success){
          console.log(content);    
          slf.doSomething();
          //若要在使用 weakSelf 时把它变成 strong 变量,可以用 __strong() 接口:
          var strongSelf = __strong(weakSelf)
      }
}))     //这里注意括号有没有包错

7. 常量、枚举、宏

  • Objective-C 里的常量/枚举不能直接在 JS 上使用,可以直接在 JS 上用具体值代替或者在 JS 上重新定义同名的全局变量.
  • Objective-C 里的同样不能直接在 JS 上使用。若定义的宏是一个值,可以在 JS 定义同样的全局变量代替,若定义的宏是程序,可以在JS展开宏.
    此外,可以通过在某个类或实例方法里将它返回,或者用添加扩展的方式提供支持
    ============举个栗子==============

新建一个文件JPMacroSupport(随便取个名)继承自JPExtension,记得在JPMacroSupport.h里导入头文件#import <JSPatchPlatform/JPEngine.h>

11139459-6EE2-4C02-BD39-DA30C1A3FE7C.png

上图中我对Masonry中的装箱宏做了扩展支持,下面将结合masonry和扩展宏进行自动布局实例操作。

//JS
   viewDidLoad: function() {
       self.ORIGviewDidLoad();      //在方法名前加 ORIG 即可调用未覆盖前的 OC 原方法
       self.masonryTest();
   },
    masonryTest:function()
    {
         require('JPEngine').addExtensions(['JPMacroSupport']);
        var view = UIView.alloc().init();
        view.setBackgroundColor(UIColor.redColor());
        self.view().addSubview(view);
        view.mas__makeConstraints(block('MASConstraintMaker *',function(make)
        {
            make.left().equalTo()(self.view()).offset()(20);
            make.right().equalTo()(self.view()).offset()(-20);
            make.top().equalTo()(self.view()).offset()(80);
            make.height().equalTo()(MASBoxValue(KScreenHeight()-100));
        }));
    },

补充一下,看到有些同学说定义宏扩展在调用的时候会报NSMallocBlock的错,因为在调用的时候忘记加括号,一定要记住加括号,比如KScreenHeight()这个

8. 其他的坑

  • 关于对象的比较
    viewA和viewB比较在OC里可以用viewA == viewB,但是在JS里进行类似的比较却是会出问题的,在OC里面相等而在JS里面未必相等,不要使用==判断。
    正确办法是用isEqual来进行viewA和viewB的比较。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,922评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,591评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,546评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,467评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,553评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,580评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,588评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,334评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,780评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,092评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,270评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,925评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,573评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,194评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,437评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,154评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352

推荐阅读更多精彩内容

  • SwiftDay011.MySwiftimport UIKitprintln("Hello Swift!")var...
    smile丽语阅读 3,830评论 0 6
  • Swift 介绍 简介 Swift 语言由苹果公司在 2014 年推出,用来撰写 OS X 和 iOS 应用程序 ...
    大L君阅读 3,204评论 3 25
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,612评论 18 399
  • 原文: https://github.com/ecomfe/spec/blob/master/javascript...
    zock阅读 3,371评论 2 36
  • 记录一些常用又不好记的快捷键(Golang 开发时经常忘记) 自动代码相关 ALT+回车 导...
    yakun0622阅读 254评论 0 1