一篇关于JSPatch, 线上直接改BUG的利器,OC语法转换时遇到的坑

  • Post author:
  • Post category:其他


开发中往往会遇到应用上线但是还是存在bug的情况,这个时候我们需要及时更新错误的方法,来进行热修复,然后在苹果将热更新禁掉后,大多时候都需要重新审核来发布新的版本更新,然后JSPatch,可以通过腾讯Bugly平台来进行线上修复方法的骚操作。对于我这种竟然出现小bug的人来说就是神器。

先上资源链接。

【链接】腾讯Bugly-一种愉悦的开发方式_平台内有应用升级的功能支持,里面有对应的JSpatch的SDK,使用很简单。


http://bugly.qq.com

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中的原方法将不会进行。



版权声明:本文为qq_15309157原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。