iOS中動態更新補丁策略JSPatch運用基礎一

iOS中動態更新補丁策略JSPatch運用基礎

        JSPatch是GitHub上一個開源的框架,其能夠經過Objective-C的run-time機制動態的使用JavaScript調用與替換項目中的Objective-C屬性與方法。其框架小巧,代碼簡潔,而且經過系統的JavaScriptCore框架與Objective-C進行交互,這使其在安全性和審覈風險上都有很強的優點。Git源碼地址:https://github.com/bang590/JSPatchgit

1、從一個官方的小demo看起

        經過cocoapods將JSPath集成進一個Xcode工程中,在AppDelegate類的中編寫以下代碼:github

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    //開始初始化引擎
    [JPEngine startEngine];
    //讀取js文件
    NSString *sourcePath = [[NSBundle mainBundle] pathForResource:@"demo" ofType:@"js"];
    NSString *script = [NSString stringWithContentsOfFile:sourcePath encoding:NSUTF8StringEncoding error:nil];
    //運行js文件
    [JPEngine evaluateScript:script];
    self.window = [[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds];
    self.window.rootViewController = [[ViewController alloc]init];
    [self.window addSubview:[self genView]];
    [self.window makeKeyAndVisible];
    return YES;
}

- (UIView *)genView
{
    UIView * view= [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 320)];
    view.backgroundColor = [UIColor redColor];
    return view;
}

在工程中添加一個js文件,編寫以下:安全

    require('UIView, UIColor, UILabel')
    //要替換函數的類
    defineClass('AppDelegate', {
            //替換函數
                //要替換函數的名稱
                genView: function() {
                    var view = self.ORIGgenView();
                    view.setBackgroundColor(UIColor.greenColor())
                    var label = UILabel.alloc().initWithFrame(view.frame());
                    label.setText("JSPatch");
                    label.setTextAlignment(1);
                    view.addSubview(label);
                    return view;
            }
    });

運行工程,能夠看到genView方法被替換成了js文件中的方法,本來紅色的視圖被修改爲了綠色。網絡

2、使用JavaScript代碼向Objective-C中修改或添加方法

        JSPatch引擎中支持3中方式進行JavaScript代碼的調用,分別是使用JavaScript字符串進行代碼運行,讀取本地的JavaScript文件進行代碼運行和獲取網絡的JavaScript文件進行代碼運行。例如,若是想要經過JavaScript代碼在項目中彈出一個警告框,在Objective-C代碼中插入以下代碼:app

- (void)viewDidLoad {
    [super viewDidLoad];
    // ‘\’符用於進行換行
    [JPEngine evaluateScript:@"\
     var alertView = require('UIAlertView').alloc().init();\
     alertView.setTitle('Alert');\
     alertView.setMessage('AlertView from js'); \
     alertView.addButtonWithTitle('OK');\
     alertView.show(); \
     "];
}

        開發者也能夠動態在Objective-C類文件中添加方法,例如在ViewController類中編寫以下:框架

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    [JPEngine startEngine];
    NSString *sourcePath = [[NSBundle mainBundle] pathForResource:@"demo" ofType:@"js"];
    NSString *script = [NSString stringWithContentsOfFile:sourcePath encoding:NSUTF8StringEncoding error:nil];
    [JPEngine evaluateScript:script];
    [self performSelectorOnMainThread:@selector(creatView) withObject:nil waitUntilDone:nil];
}

JavaScript文件代碼以下:函數

 require('UIView, UIColor, UILabel')
    defineClass('ViewController', {
            // replace the -genView method
                creatView: function() {
                    var view = UIView.alloc().initWithFrame({x:20, y:20, width:100, height:100});
                    view.setBackgroundColor(UIColor.greenColor());
                    var label = UILabel.alloc().initWithFrame({x:0, y:0, width:100, height:100});
                    label.setText("JSPatch");
                    label.setTextAlignment(1);
                    view.addSubview(label);
                self.view().addSubview(view)
            }
    });

除了上面的代碼,在ViewController.m文件中沒有編寫任何其餘的方法,運行工程,能夠看到程序並無崩潰,ViewController執行了creatView方法。ui

        經過上面的示例,咱們發現使用JSPatch能夠作一些十分有趣的事。對於iOS應用來講,經過官方渠道AppStore進行應用程序的發佈要經過人工審覈,有時這個審覈週期會很是長,若是在開發者在編寫代碼時留下了一些小漏洞,應用一旦上線,若要修改掉這個bug就十分艱難了。有了JSPatch,咱們能夠想象,若是能夠定位到線上應用有問題的方法,使用JS文件來修改掉這個方法,這將是多麼cool的一件事,事實上,JSPatch的主要用途也是能夠實現線上應用極小問題的hotfix。lua

3、JavaScript與Objective-C交互的基礎方法

        要使用JSPatch來進行Objective-C風格的方法編寫,須要遵照一些JavaScript與Objective-C交互的規則。spa

1.在JavaScript文件中使用Objective-C類

   在編寫JavaScript代碼時若是須要用到Objective-C的類,必須先對這個類進行require引用,例如,若是須要使用UIView這個類,須要在使用前進行以下引用:

require('UIView')

一樣也能夠一次對多個Objective-C類進行引用:

require('UIView, UIColor, UILabel')

還有一種更加簡便的寫法,直接在使用的時候對其進行引用:

require('UIView').alloc().init()

2.在JavaScript文件中進行Objective-C方法的調用

    在進行Objective-C方法的調用時,分爲兩種,一種是調用類方法,一種是調用類的對象方法。

調用類方法:經過類名打點的方式來調用類方法,格式相似以下,括號內爲參數傳遞:

UIColor.redColor()

調用實例方法:經過對象打點的方式調用類的實例方法,格式以下,括號內爲參數傳遞:

view.addSubview(label)

對於Objective-C中的多參數方法,轉化爲JavaScript將參數分割的位置以_進行分割,參數所有放入後面的括號中,以逗號分割,示例以下:

view.setBackgroundColor(UIColor.colorWithRed_green_blue_alpha(0,0.5,0.5,1))

對於Objective-C類的屬性變量,在JavaScript中只能使用getter與setter方法來訪問,示例以下:

label.setText("JSPatch")

提示:若是原Objective-C的方法中已經包含了_符號,則在JavaScript中使用__代替。

3.在JavaScript中操做與修改Objective-C類

    JSPatch的最大應用是在應用運行時動態的操做和修改類。

重寫或者添加類的方法:

在JavaScript中使用defineClass來定義和修改類中的方法,其編寫格式以下所示:

/*
classDeclaration:要添加或者重寫方法的類名 字符串  若是此類不存在 則會建立新的類
instanceMethods:要添加或者重寫的實例方法 {}
classMethods:要添加或者重寫的類方法 {}
*/
defineClass(classDeclaration, instanceMethods, classMethods)

示例以下:

defineClass('ViewController', {
            // replace the -genView method
                newFunc: function() {
                    //編寫實例方法
                    self.view().setBackgroundColor(UIColor.redColor())
                }
    
            },{

                myLoad:function(){
                    //編寫類方法
                }

            }
            )

若是在重寫了類中的方法後要調用原方法,須要使用ORIG前綴,示例以下:

defineClass('ViewController', {
            // replace the -genView method
                viewDidLoad: function() {
                    //編寫實例方法
                    self.ORIGviewDidLoad()
                }
    
            }
            )

對於Objective-C中super關鍵字調用的方法,在JavaScript中可使用self.super()來調用,例如:

defineClass('ViewController', {
            // replace the -genView method
                viewDidLoad: function() {
                    //編寫實例方法
                    self.super().viewDidLoad()
                }
    
            }
            )

一樣JSPatch也能夠爲類添加臨時屬性,用於在方法間參數傳遞,使用set_Prop_forKey()來添加屬性,使用getProp()來獲取屬性,注意,JSPatch添加的屬性不能使用Objective-C的setter與getter方法訪問,以下:

defineClass('ViewController', {
            // replace the -genView method
                viewDidLoad: function() {
                    //編寫實例方法
                    self.super().viewDidLoad()
                    self.setProp_forKey("JSPatch", "data")
                },
                touchesBegan_withEvent(id,touch){
                    self.getProp("data")
                    self.view().setBackgroundColor(UIColor.redColor())
                }
    
            }
            )

關於爲類添加協議的遵照,和Objective-C中遵照協議的方式一致,以下:

defineClass("ViewController2: UIViewController <UIAlertViewDelegate>", {
            viewDidAppear: function(animated) {
            var alertView = require('UIAlertView')
            .alloc()
            .initWithTitle_message_delegate_cancelButtonTitle_otherButtonTitles(
                                                                                "Alert",
                                                                                "content",
                                                                                self,
                                                                                "OK",
                                                                                null
                                                                                )
            alertView.show()
            },
            alertView_clickedButtonAtIndex:function(alertView, buttonIndex) {
            console.log('clicked index ' + buttonIndex)
            }
            })

 

專一技術,熱愛生活,交流技術,也作朋友。

——琿少 QQ羣:203317592

相關文章
相關標籤/搜索