JSPatch 踩坑日記

寫給蠢蠢的自己

一定要記得時刻加括號括號括號()()()啊小伙伴們 一些小細節(jié)坑了我一下午



1. 私有成員變量
============舉個栗子==============

// OC
@implementation JPTestViewController { 
       BOOL _hasRead;
}
@end
// JS
defineClass("JPTableViewController", { 
      viewDidLoad: function() {
          var hasRead = self.valueForKey("_hasRead");         //獲取成員變量
          self.setValue_forKey(false, "_hasRead");            //設(shè)置成員變量
      },
});

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 支持調(diào)用 stringWithFormat,不過所有參數(shù)類型都需改為 %@:
//OC
[NSString stringWithFormat:@"name:%@, age:%d", @"alex", 12];
//JS
NSString.stringWithFormat("name:%@, age:%@", "alex", 12);
  • 方法二 JS語法拼接
    雖然支持 stringWithFormat,但還是建議使用 JS 語法拼接字符串:
    <u>(此處有坑 切記要將字符串轉(zhuǎn)為JS對象才能操作這些類型,詳情請見下面的字符串 / 數(shù)組 / 字典 操作問題)</u>
//JS
var ret = "name:" + "alex" + " age:(" + 12 + ")"; 
//注:JS中將int類型轉(zhuǎn)為字符串方法為: 
var age = 12;
var ageStr = 12+"";

4. NSNumber 相關(guān)問題
NSNumber 與上述四個類型不一樣,所有數(shù)值類型以及 NSNumber 對象到 JS 后都會變成數(shù)值,不能再調(diào)用這個數(shù)值的任何方法:
翻譯過來就是 JS里不存在NSNumber這玩意兒!只能當數(shù)值用 所有跟NSNumber有關(guān)的方法例如isEqualToNumber或者stringValue等都不能用。

5. 字符串 / 數(shù)組 / 字典 操作問題
剛使用 JSPatch 經(jīng)常會對 NSString / NSArray / NSDictionary / NSDate 這四個類的使用感到迷惑,因為 JS 語言本身有對應(yīng)的這四個類型,會跟 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 對象,可以調(diào)用 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 對象,不能調(diào)用 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() 接口轉(zhuǎn)為 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() 接口轉(zhuǎn)為 JS 對象,可以用 JS語法操作
var transArr = ocUsers.toJS();
var firstUser = transArr[0];
for (var i = 0; i < transArr.length; i ++) {       //注:JS中數(shù)組長度為.length不是.count
        var user = transArr[i];
}
//錯誤: ocInfo 不是 JS 對象,不能用[]語法
var v = ocInfo['k'];
//正確:已用 .toJS() 接口轉(zhuǎn)為 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轉(zhuǎn)成JS對象 進行拼接
        return true;
},

既然講到了NSArray,這里順便講講數(shù)組遍歷for...in
首先從 OC 返回的 NSArray / NSDictionary 對象是不能直接用 for...in 遍歷的,需要調(diào)用 .toJS() 后才能進行遍歷,詳情見上文。
然后在遍歷數(shù)組時,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,表示遍歷數(shù)組的序號 
      console.log(arr[o]); //輸出 name age,這樣才表示數(shù)組的值
}

============再來個栗子==============

    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();
        }
        //錯誤原因:需要調(diào)用.JS()才能使用for...in方法遍歷 如用OC對象 則可以使用以下方法遍歷
        for (int i = 0; index < cell.contentView().subviews().count(); i++) { 
            var subView = cell.contentView().subviews().objectAtIndex(i);
            subView.removeFromSuperview();
        }
        //或者可以使用以下方法 講OC對象轉(zhuǎn)成JS對象進行for...in遍歷
        //此處有坑 錯誤二
        var subViews = cell.contentView().subviews().toJS();
        for (var a in subViews) {
            var.removeFromSuperview();    //?錯誤 JS中輸入的var表示遍歷數(shù)組的序號 會crash
        }
        //正確姿勢
        for (var a in subViews) {
            subViews[a].removeFromSuperview();
        }
        cell.setBackgroundColor(UIColor.whiteColor());
        cell.textLabel().setText("");
        return cell;
    },

6. Block
當要把 JS 函數(shù)作為 block 參數(shù)給 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展開宏.
    此外,可以通過在某個類或?qū)嵗椒ɡ飳⑺祷兀蛘哂锰砑訑U展的方式提供支持
    ============舉個栗子==============

新建一個文件JPMacroSupport(隨便取個名)繼承自JPExtension,記得在JPMacroSupport.h里導入頭文件#import <JSPatchPlatform/JPEngine.h>

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

上圖中我對Masonry中的裝箱宏做了擴展支持,下面將結(jié)合masonry和擴展宏進行自動布局實例操作。

//JS
   viewDidLoad: function() {
       self.ORIGviewDidLoad();      //在方法名前加 ORIG 即可調(diào)用未覆蓋前的 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));
        }));
    },

補充一下,看到有些同學說定義宏擴展在調(diào)用的時候會報NSMallocBlock的錯,因為在調(diào)用的時候忘記加括號,一定要記住加括號,比如KScreenHeight()這個

8. 其他的坑

  • 關(guān)于對象的比較
    viewA和viewB比較在OC里可以用viewA == viewB,但是在JS里進行類似的比較卻是會出問題的,在OC里面相等而在JS里面未必相等,不要使用==判斷。
    正確辦法是用isEqual來進行viewA和viewB的比較。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,882評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,208評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,746評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,666評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,477評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,960評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,047評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,200評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,726評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,617評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,807評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,327評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,049評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,425評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,674評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,432評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,769評論 2 372

推薦閱讀更多精彩內(nèi)容