开发中往往会遇到应用上线但是还是存在bug的情况,这个时候我们需要及时更新错误的方法,来进行热修复,然后在苹果将热更新禁掉后,大多时候都需要重新审核来发布新的版本更新,然后JSPatch,可以通过腾讯Bugly平台来进行线上修复方法的骚操作。对于我这种竟然出现小bug的人来说就是神器。
先上资源链接。
【链接】腾讯Bugly-一种愉悦的开发方式_平台内有应用升级的功能支持,里面有对应的JSpatch的SDK,使用很简单。
JSPatch-基础用法,里面有相关OC语法的转换,但是不是很全,遇到了很多坑。
https://github.com/bang590/JSPatch/wiki/JSPatch
OC转换JS语法转换器。
http://www.jspatch.com/Tools/convertor
接下来我会详细说明一下我在使用时遇到的问题。
之前项目中因为请求下来的数据没有做分类,导致了一些不需要的数据展示,然而项目已经上线,只能通过热修复去修复对应的方法。
下面的代码是我修改之后的正确OC方法。
- (NSInteger)sheetView:(SheetView *)sheetView numberOfRowsInSection:(NSInteger)section
{
NSMutableArray *data = [[NSMutableArray alloc] initWithCapacity:0];
if ([self.logFuncName isEqualToString:@"基站故障衍生告警查询结果"]) {
for (int i = 0; i<dataSource.count; i++) {
NSDictionary *dic = [datas objectAtIndex:i];
NSDictionary *dic2 = [dataSource objectAtIndex:i];
NSString *is_derive = [dic objectForKey:@"Is_derive"];
if ([is_derive isEqualToString:@"0"]) {
} else {
[data addObject:dic2];
}
}
if (dataSource.count == datas.count) {
dataSource = [data mutableCopy];
}
if (screeningDatas.count != 0) {
return screeningDatas.count;
} else {
return dataSource.count;
}
} else {
if (screeningDatas.count != 0) {
return screeningDatas.count;
} else {
return dataSource.count;
}
}
}
上面的方法很麻烦,但是是应急的一种方法,因为在转换变成语言的时候,是按照方法横数来的,我只能从这个小方法里面改最容易。 这个方法在OC语言中是完全没问题的。然后我通过JS语法转换之后生成了下面的代码。
require("NSMutableArray");
defineClass("(方法所在控制器的类名)", {
sheetView_numberOfRowsInSection: function(sheetView, section) {
var data = NSMutableArray.alloc().initWithCapacity(0);
if (self.logFuncName().isEqualToString("基站故障衍生告警查询结果")) {
for (var i = 0; i < dataSource.count(); i++) {
var dic = datas.objectAtIndex(i);
var dic2 = dataSource.objectAtIndex(i);
var is_derive = dic.objectForKey("Is_derive");
if (is_derive.isEqualToString("0")) {} else {
data.addObject(dic2);
}
}
if (dataSource.count() == datas.count()) {
dataSource = data.mutableCopy();
}
if (screeningDatas.count() != 0) {
return screeningDatas.count();
} else {
return dataSource.count();
}
} else {
if (screeningDatas.count() != 0) {
return screeningDatas.count();
} else {
return dataSource.count();
}
}
}
}, {});
转换之后,转换器没有给我提示报错,但是在部署代码时,怎么都无法运行,后来找到了原因,这里我要强调一下,因为JS语法中的字典和数组是没有[]方法的所以不能使用array[i]的方法,而要使用[datas objectAtIndex:i],同理字典也是没有[]方法的。
上面代码存在了两个问题。
1.私有变量没有处理。这个方法中的,dataSource,datas,screeningDatas,都是所在类的私有属性。在这里是需要做特殊处理的,要先定义一个指针去获取私有变量的地址,才能在方法中使用。
2.self.logFuncName(),logFuncName是当前控制器父类的私有变量,也就是我的基类里面的变量,如果要使用,需要在第一行代码中require(“NSMutableArray”);加入对应类的名字–>require(“NSMutableArray, (类名)”);
修改之后的JS代码如下:
require("NSMutableArray");
defineClass("GDSearchResultsVC", {
sheetView_numberOfRowsInSection: function(sheetView, section) {
var data = NSMutableArray.alloc().initWithCapacity(0);
var dataSource = self.valueForKey("dataSource");
var datas = self.valueForKey("datas");
var screeningDatas = self.valueForKey("screeningDatas");
var columns = self.valueForKey("columns");
if (columns.count() == 4) {
for (var i = 0; i < dataSource.count(); i++) {
var dic = datas.objectAtIndex(i);
var dic2 = dataSource.objectAtIndex(i);
var is_derive = dic.objectForKey("Is_derive");
if (is_derive.isEqualToString("0")) {} else {
data.addObject(dic2);
}
}
if (dataSource.count() == datas.count()) {
dataSource = data.mutableCopy();
}
self.setValue_forKey(screeningDatas, "screeningDatas");
self.setValue_forKey(dataSource, "dataSource");
if (screeningDatas.count() != 0) {
return screeningDatas.count();
} else {
return dataSource.count();
}
} else {
if (screeningDatas.count() != 0) {
return screeningDatas.count();
} else {
return dataSource.count();
}
}
}
}, {});
OC转JS语法中还有其他的比较容易遇到的坑:
1.JS数组、字典、字符串等等和OC的都不一样
2.JS不能识别枚举
_pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:0];
UIPageViewControllerTransitionStylePageCurl和UIPageViewControllerNavigationOrientationHorizontal全部都不能识别,所以都直接改成对应的枚举值。
3.CGRect也是一个坑,最多的是在设置frame的时候,比如:
view.frame.origin.x
在js应该写成:view.frame().x (特别注意不是view.frame().origin().x)
CGRectMake(20, 20, 100, 100)
应该写成:{x:20, y:20, width:100, height:100}
4.GCD写法转换
//OC
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(30.f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
_timer = [NSTimer scheduledTimerWithTimeInterval:TimeInterval target:self selector:@selector(repeat) userInfo:nil repeats:YES];
[_timer fire];
});
这里在转换成JS语法时,要注意两点,在JS语法中dispatch_after(dispatch_time())已经做了处理,所以直接使用JS语法的dispatch_after即可,然后30.f这里要注意,JS语法时没有.f的方法的。 所以要写成30.0。然后TimeInterval这里时全局变量,可以直接换成对应的值。_timer时私有变量要使用self.valueForKey和self.setValue_forKey去处理。
//JS
var timer = self.valueForKey("_timer");
dispatch_after(30.0, function(){
timer = NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats(300, self, "repeat", null, YES);
timer.fire();
self.setValue_forKey(timer, "_timer");
});
5.JS数组与OC数组字典等处理
var keys = [ "failureoccurtime", "appendtime", "clearAlarmTime", "appendtype", "netcellname", "businessstate", "subalarminfo", "alarmobj", "alarmname" ];
var titles = [ "故障发生时间", "追加时间", "告警消除时间", "追加类型", "网元名称", "业务状态", "子告警信息", "子告警对象", "告警名称" ];
var arr = NSMutableArray.new();
var dic = subAlarm.objectAtIndex(indexPath.row());
for (var i = 0; i < 9; i++) {
var key = keys[i];
var info = dic[key];
if (info == null || info.isKindOfClass(NSNull.class())) {
info = "";
}
arr.addObject(NSString.stringWithFormat("%@:%@", titles[i], info));
}
keys和titles,dic等都是JS数组字典,不能使用OC的objec的方法,可以直接使用[]方法调用。
6.if判断的坑
当判断字符串的时候要使用info == false 而不能用 info == null 不好使。
7.C函数的转换
第一个参数是函数名,第二个参数是返回值类型和参数类型,用字符串表示,第一个类型为返回值,后续为参数类型。例如这样一个C函数:
int funcA(void *ptr, NSObject *obj, float num) {}
需要这样定义:
defineCFunction("funcA", "int, void *, NSObject *, float")
目前支持的参数类型有:
id
,
BOOL
,
int
,
void
,
char
,
short
,
unsigned short
,
unsigned int
,
long
,
unsigned long
,
long long
,
unsigned long long
,
float
,
double
,
bool
,
size_t
,
CGFloat
,
NSInteger
,
Class
,
void*
。
调试:
在进行JSPatch是否生效调试过程中,我是通过苹果自带的Safari去调试的。
首先打开Safari浏览器: Safari浏览器 -> 偏好设置 -> 高级最下面,显示开发选项进行勾选。
然后运行模拟器,如果需要真机调试,请在手机上打开对应设置,在设置中找到Safari 浏览器 -> 高级 -> Web 检查器 ->打开开关。
然后运行对应的版本号,build对应的程序。
点开JSContext就可以看到对应的下载文件了,里面JSPatch.js是SDK,main.js是我们部署的补丁文件。运行到对应的控制器的方法的时候,应用就会直接走main.js的替换方法了,OC中的原方法将不会进行。