写给蠢蠢的自己
一定要记得时刻加括号括号括号()()()啊小伙伴们 一些小细节坑了我一下午
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 语法拼接字符串:
(此处有坑 切记要将字符串转为JS对象才能操作这些类型,详情请见下面的字符串 / 数组 / 字典 操作问题)
//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 的这四个类混淆。要避免混淆,要弄清楚两点:
- 需要认清这四个类有 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
- 若要用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
上图中我对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的比较。