寫給蠢蠢的自己
一定要記得時刻加括號括號括號()()()啊小伙伴們 一些小細節(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 的這四個類混淆。要避免混淆,要弄清楚兩點:
- 需要認清這四個類有 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
- 若要用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>
上圖中我對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的比較。