JSPatchの簡単整理
16135 ワード
これを簡単に整理したのは、いくつかの必要があるからです.実は著者の文書には、原理が含まれています.ほとんどは直接抜粋しています.
bankg 590/JSPatch
JSPatchはiOSのダイナミックアップデートフレームで、プロジェクトに極小エンジンを導入するだけで、JavaScriptを使って任意のObjective-C原生インターフェースを呼び出すことができ、スクリプト言語の利点を得ることができます.プロジェクトの動的な追加モジュール、またはプロジェクトの元コードを置き換えるために動的にバグを修復することができます.
1.プロセス
主なプロセスは、JPプラットフォーム(または自分のバックグラウンド)でパッチJSファイルをリリースし、次の送信方法(フルボリューム、グレースケール、開発など)を選択することです.クライアントはJPSDKにアクセスし(または自分のサービスのダウンロード更新ロジックを使用して)、最新のjsを自動的に更新してダウンロードして実行します.
2.原理
1).基礎原理
Objective-C Runtimeにより運転中にクラス名/メソッド名で反射して該当するクラスと方法を得ることができます.は、ある種類の方法を置き換えることもできます. はまた、クラスの追加方法として、新しいクラスを登録することができます. 基本的な原理は、JSがOCに文字列を伝え、OCがRuntimeインターフェースを通じてOCを呼び出し、置換する方法です.
2)JSインターフェース実現
まず、JSでOCメソッドを呼び出すにはどうすればいいですか?
正規表現により、すべての方法を呼び出し
JSオブジェクトベースのObjectのprototypeに
具体的な方法:OCローディング
JSインターフェースが完成したら、OCに伝達して使うJavaScripCoreのインターフェースをします.JP Engineが起動する時、対応する実行ブロックを定義します.js中の
ここでは、パラメータの理由を考慮して、簡単に
-forwardingTarget ForSelector:
-methodSignature ForSelector:,-forwardInvocation:などの方法です.
第3段階
具体的には、UIView Controllerの-view Willapearを置き換える方法を例に挙げます.は、UIView Controllerの はUID View Controllerに UID View Controllerを書き換える 呼び出しプロシージャ
3.使用
詳細なJSPatchの使い方はドキュメントに具体的に紹介されていますが、OCの利用過程で必要とされるほとんどの状況をカバーしています.
JSPatchの基礎的な使い方
以下は比較的によく使われるいくつかの種類です.
1.require
Objective-Cクラスを使用する前に呼び出しが必要です.クラスの方法を呼び出す 起動方法例 Propertyを取得/修正することは、このPropertyを呼び出すgetter/setter方法に等しい. メソッド名変換マルチパラメータ方法名使用 カバー実例方法 方法名の前にORIGを加えると、カバーされていない前のOC元の方法が起動されます. 種類の方法 カバレッジCategory方法は、通常の方法をカバーするのと同じである. は、 を起動する.動的追加Propertyは、defineClass()の第二のパラメータがクラスのためにpropertyを追加し、フォーマットは文字列配列であり、使用時はOC propertyインターフェースと一致する: Protocolは、定義時に、クラスにいくつかのProtocolインターフェースを実現させることができます.書き方はOCと同じです. Struct JSPatch原生はCGRect/CGPoid/CGS ize/NSRangeの4つのstructタイプをサポートし、JSオブジェクトで表します. Selector JSで文字列を使ってSelectorを表します.
JS上のnullとundefinedはOCのnilを表しています.NSNullを表すなら、nsnullを使います.NULLを表すなら、nullを使います.
6.NSAray/NSString/NSDictionary
NSAray/NSString/NSDictionaryは、自動的に対応するJSタイプに変換されず、通常のNSObjectのように使用される.
ブロック転送
JS関数をblockパラメータとしてOCに渡すには、まずblockインターフェースで包装する必要があります.
OCからJSに戻るブロックは自動的にJS functionに変わります.直接起動すればいいです.
blockにself変数を使う
blockでself変数が使えません.blockに入る前に臨時変数を使って保存する必要があります.
JSからblockにOCには二つの制限があります.
A.blockパラメータの個数は最大6個までサポートします.(より多くのサポートが必要なら、ソースを変更することができます)B.blockパラメータのタイプはdoubleではありません.
また、JSパッケージに対応していないblockがOCに伝わってからJSに呼び戻される(原因はissue落155参照):
iOSに内蔵されているダイナミックライブラリについては、元のAPPにロードされていない場合、SafariServices.frame ebookをロードするために、以下のように動的にロードすることができます.
9.デバッグ
一つのオブジェクトを印刷するためにconsone.log()を使用することができます.NSLog()に相当します.直接にXCodeコンソールに打ちます.
consolie.log()は任意のパラメータをサポートしていますが、NSLogのようなNSLog(@「num:%f」1.0)のパッチはサポートされていません.
4.JPSDK統合関連API
bankg 590/JSPatch
JSPatchはiOSのダイナミックアップデートフレームで、プロジェクトに極小エンジンを導入するだけで、JavaScriptを使って任意のObjective-C原生インターフェースを呼び出すことができ、スクリプト言語の利点を得ることができます.プロジェクトの動的な追加モジュール、またはプロジェクトの元コードを置き換えるために動的にバグを修復することができます.
1.プロセス
主なプロセスは、JPプラットフォーム(または自分のバックグラウンド)でパッチJSファイルをリリースし、次の送信方法(フルボリューム、グレースケール、開発など)を選択することです.クライアントはJPSDKにアクセスし(または自分のサービスのダウンロード更新ロジックを使用して)、最新のjsを自動的に更新してダウンロードして実行します.
2.原理
1).基礎原理
Objective-C Runtimeにより運転中にクラス名/メソッド名で反射して該当するクラスと方法を得ることができます.
2)JSインターフェース実現
まず、JSでOCメソッドを呼び出すにはどうすればいいですか?
require('UIView');
var view = UIView.alloc().init();
jsでは、このような構文はエラーとして報告されます.著者らのやり方は、パッチmain.jsファイルをロードする前に、JSPatch.js
に匿名の自己実行関数をロードしたものです.主な役割はこれらのインターフェースを置き換えるためです.正規表現により、すべての方法を呼び出し
__c()
関数に変更しました.JSオブジェクトベースのObjectのprototypeに
__c
のメンバーを加えると、すべてのオブジェクトが__c
に呼び出され、現在のオブジェクトタイプの判断によって異なる動作が行われる.具体的な方法:OCローディング
main.js
において、jsコードが修正され、この匿名関数が呼び出され、正規表現を利用して__c
の呼び出しが増加した. NSString *formatedScript = [NSString stringWithFormat:
@";(function(){try{%@}catch(e){_OC_catch(e.message, e.stack)}})();",
[_regex stringByReplacingMatchesInString:script options:0 range:NSMakeRange(0, script.length) withTemplate:_replaceStr]];
したがって、main.js
の実行時には、次のように変換されており、alloc
、init
は文字列に変換される.;(function(){
try{require('UIView');
var view = UIView.__c("alloc")().__c("init")();
}
catch(e){
_OC_catch(e.message, e.stack)
}
})();
3)メッセージ伝達JSインターフェースが完成したら、OCに伝達して使うJavaScripCoreのインターフェースをします.JP Engineが起動する時、対応する実行ブロックを定義します.js中の
definClass
メソッドの置き換えを実行すると、runtimeが起動され、方法が置換される.context[@"_OC_defineClass"] = ^(NSString *classDeclaration, JSValue *instanceMethods, JSValue *classMethods)
{
return defineClass(classDeclaration, instanceMethods, classMethods);
};
context[@"_OC_defineProtocol"] = ^(NSString *protocolDeclaration, JSValue *instProtocol, JSValue *clsProtocol)
{
return defineProtocol(protocolDeclaration, instProtocol,clsProtocol);
};
4)方法置換ここでは、パラメータの理由を考慮して、簡単に
class_replaceMethod
を使用して方法の置き換えが行われない.NSObjectオブジェクトが存在しない方法を呼び出すと、すぐに異常を投げずに、複数の転送を経て、オブジェクトのレノベInstance Methodを呼び出します.-forwardingTarget ForSelector:
-methodSignature ForSelector:,-forwardInvocation:などの方法です.
第3段階
-forwardInvocation:
にはNSInvocationオブジェクトがあり、このNSInvocationオブジェクトは、Selector名、パラメータ、および戻り値タイプを含むこの方法で呼び出されたすべての情報を保持しています.最も重要なのは、全パラメータ値があり、このNSInvocationオブジェクトから呼び出されるすべてのパラメータ値を持つことです.具体的には、UIView Controllerの-view Willapearを置き換える方法を例に挙げます.
-viewWillAppear:
方法をclass_replaceMethod()
インターフェースを介して_objc_msgForward
に向けて指す.これは大域IMPであり、OC呼び出し方法が存在しない場合は、このIMPに転送され、ここで直接に方法をこのIMPに置き換え、この方法を呼び出すと-forwardInvocation:
に進む.-ORIGviewWillAppear:
および-_JPviewWillAppear:
の2つの方法を追加し、前者は元のIMP実装を指し、後者は新しい実装であり、後にこの実装にJS関数をフィードバックする.-forwardInvocation:
方法は、カスタム実装である.一旦OCでUIView Controllerの-viewWillAppear:
方法が起動されると、上記の処理によって-forwardInvocation:
にこの呼び出しが転送され、このときにはNSInvocationが組み込まれていて、この呼び出しのパラメータが含まれています.ここでは、NSInvocationからパラメータを逆に解き、パラメータを持って上記の新たな増加の方法-JPviewWillAppear:
を呼び出し、この新しい方法でパラメータを取ってJSに伝え、JSの実現関数を呼び出します.コール全体が終了します.3.使用
詳細なJSPatchの使い方はドキュメントに具体的に紹介されていますが、OCの利用過程で必要とされるほとんどの状況をカバーしています.
JSPatchの基礎的な使い方
以下は比較的によく使われるいくつかの種類です.
1.require
Objective-Cクラスを使用する前に呼び出しが必要です.
require('UIView, UIColor')
var view = UIView.alloc().init()
var red = UIColor.redColor()
または直接使用時にレギュイルを呼び出します.require('UIView').alloc().init()
2.呼び出し方法var redColor = UIColor.redColor();
var view = UIView.alloc().init();
view.setNeedsLayout();
view.setBackgroundColor(redColor);
var bgColor = view.backgroundColor();
require('className’)
分離:var indexPath = require('NSIndexPath').indexPathForRow_inSection(0, 1);
3.defineClass_
@param classDeclaration: , / Protocol
@param properties: property, ,
@param instanceMethods:
@param classMethods:
// viewDidAppear
defineClass("HYWebViewController", {
viewDidAppear: function(animated) {
self.super().viewDidAppear(animated);
console.log("JS---viewDidAppear---jspatch test");
},
});
// JS
defineClass("HYWebViewController", {
viewDidLoad: function() {
self.ORIGviewDidLoad();
},
})
// JS
defineClass("JPTableViewController", {
//
}, {
//
shareInstance: function() {
...
},
})
defineClass(classDeclaration, [properties,] instanceMethods, classMethods)
インターフェースを使用してsuperキーワードを表し、super方法defineClass("JPTableViewController", ['data', 'totalCount'], {
init: function() {
self = self.super().init()
self.setData(["a", "b"]) // Property (id data)
self.setTotalCount(2)
return self
},
viewDidLoad: function() {
var data = self.data() // Property
var totalCount = self.totalCount()
},
})
defineClass("JPViewController: UIViewController", {
})
このようにする役割は、Protoclで定義されている方法を追加すると、クラスには実現されていない方法がある場合、パラメータタイプはすべてidではなく、自動的にProtocolで定義されているタイプに変わります.// objc
@protocol UIAlertViewDelegate
...
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex;
...
@end
// js
defineClass("JPViewController: UIViewController ", {
viewDidAppear: function(animated) {
var alertView = require('UIAlertView').alloc().initWithTitle_message_delegate_cancelButtonTitle_otherButtonTitles(
"Alert",self.dataSource().objectAtIndex(indexPath.row()),self, "OK", null)
alertView.show()
}
alertView_clickedButtonAtIndex: function(alertView, buttonIndex) {
console.log('clicked index ' + buttonIndex)
}
})
4.特殊タイプ// Obj-C
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(20, 20, 100, 100)];
[view setCenter:CGPointMake(10,10)];
[view sizeThatFits:CGSizeMake(100, 100)];
CGFloat x = view.frame.origin.x;
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})
view.sizeThatFits({width: 100, height:100})
var x = view.frame().x
var range = {location: 0, length: 1}
//Obj-C
[self performSelector:@selector(viewWillAppear:) withObject:@(YES)];
//JS
self.performSelector_withObject("viewWillAppear:", 1)
5.nilJS上のnullとundefinedはOCのnilを表しています.NSNullを表すなら、nsnullを使います.NULLを表すなら、nullを使います.
6.NSAray/NSString/NSDictionary
NSAray/NSString/NSDictionaryは、自動的に対応するJSタイプに変換されず、通常のNSObjectのように使用される.
//Obj-C
@implementation JPObject
+ (NSArray *)data
{
return @[[NSMutableString stringWithString:@"JS"]]
}
+ (NSMutableDictionary *)dict
{
return [[NSMutableDictionary alloc] init];
}
@end
// JS
require('JPObject')
var ocStr = JPObject.data().objectAtIndex(0)
ocStr.appendString("Patch") // oc
var dict = JPObject.dict()
dict.setObject_forKey(ocStr, 'name')
console.log(dict.objectForKey('name')) //output: JSPatch
NSAray/NSString/NSDictionaryを対応するJSタイプに変換するには.toJS()インターフェース:// JS
var data = require('JPObject').data().toJS()
//data instanceof Array === true
data.push("Patch") // js
var dict = JPObject.dict()
dict.setObject_forKey(data.join(''), 'name')
dict = dict.toJS()
console.log(dict['name']) //output: JSPatch
7.ブロックブロック転送
JS関数をblockパラメータとしてOCに渡すには、まずblockインターフェースで包装する必要があります.
// Obj-C
@implementation JPObject
+ (void)request:(void(^)(NSString *content, BOOL success))callback
{
callback(@"I'm content", YES);
}
@end
// JS
require('JPObject').request(block("NSString *, BOOL", function(ctn, succ) {
if (succ) log(ctn) //output: I'm content
}))
ここでblockのパラメータの種類を文字列で表します.このblockの各パラメータの種類を書いて、カンマで区切ります.NSObjectオブジェクトは、NSString*のように、NSArryなどはidで表されるが、blockオブジェクトはNSBlockで表される.OCからJSに戻るブロックは自動的にJS functionに変わります.直接起動すればいいです.
// Obj-C
@implementation JPObject
typedef void (^JSBlock)(NSDictionary *dict);
+ (JSBlock)genBlock
{
NSString *ctn = @"JSPatch";
JSBlock block = ^(NSDictionary *dict) {
NSLog(@"I'm %@, version: %@", ctn, dict[@"v"])
};
return block;
}
+ (void)execBlock:(JSBlock)blk
{
}
@end
// JS
var blk = require('JPObject').genBlock();
blk({v: "0.0.1"}); //output: I'm JSPatch, version: 0.0.1
もしこれをOCから届いたブロックをOCに戻したいなら、同じようにblockで包装する必要があります.ここでblkはもう普通のJS functionです.私達が定義したJS functionと同じです.// JS
var blk = require('JPObject').genBlock();
blk({v: "0.0.1"}); //output: I'm JSPatch, version: 0.0.1
require('JPObject').execBlock(block("id", blk));
まとめ:JSにはblockタイプの変数がありません.OCのblockオブジェクトはJSに伝わってJS functionになります.JSからblockをOCに送るには、すべてblock()インターフェースで包装する必要があります.blockにself変数を使う
blockでself変数が使えません.blockに入る前に臨時変数を使って保存する必要があります.
defineClass("JPViewController", {
viewDidLoad: function() {
var slf = self;
require("JPTestObject").callBlock(block(function(){
//`self` is not available here, use `slf` instead.
slf.doSomething();
});
}
}
制限JSからblockにOCには二つの制限があります.
A.blockパラメータの個数は最大6個までサポートします.(より多くのサポートが必要なら、ソースを変更することができます)B.blockパラメータのタイプはdoubleではありません.
また、JSパッケージに対応していないblockがOCに伝わってからJSに呼び戻される(原因はissue落155参照):
- (void)callBlock:(void(^)(NSString *str))block {
}
defineClass('JPTestObject', {
run: function() {
self.callBlock(block('NSString*', function(str) {
console.log(str);
}));
},
callBlock: function(blk) {
//blk block run JS OC , 。
blk("test block");
}
});
8.ダイナミックライブラリの読み込みiOSに内蔵されているダイナミックライブラリについては、元のAPPにロードされていない場合、SafariServices.frame ebookをロードするために、以下のように動的にロードすることができます.
var bundle = NSBundle.bundleWithPath("/System/Library/Frameworks/SafariServices.framework");
bundle.load();
ローディングしたらSafariServices.frame ebookが使えます.9.デバッグ
一つのオブジェクトを印刷するためにconsone.log()を使用することができます.NSLog()に相当します.直接にXCodeコンソールに打ちます.
consolie.log()は任意のパラメータをサポートしていますが、NSLogのようなNSLog(@「num:%f」1.0)のパッチはサポートされていません.
var view = UIView.alloc().init();
var str = "test";
var num = 1;
console.log(view, str, num)
console.log(str + num); // JS
SafariのデバッグツールでJSをブレークポイントデバッグすることもできます.詳細はJSのブレークポイントデバッグを参照してください.4.JPSDK統合関連API
//
[JSPatch setLogger:^(NSString *msg) {
// msg JSPatch log , logger
YOUR_APP_LOG(@"%@", msg);
}];
/*
+testScriptInBundle main.js
+startWithAppKey:
*/
[JSPatch testScriptInBundle];
/**
JSPatch
// // // // //
*/
[JSPatch setupCallback:^(JPCallbackType type, NSDictionary *data, NSError *error) {
switch (type) {
case JPCallbackTypeUpdate: {
NSLog(@"updated %@ %@", data, error);
break;
}
case JPCallbackTypeRunScript: {
NSLog(@"run script %@ %@", data, error);
break;
}
default:
break;
}
}];
/**
sync
*/
[JSPatch setupUserData:@{
@"userId": @"100867",
@"location": @"guangdong"
}];
/**
RSA key, +sync: ,
*/
[JSPatch setupRSAPublicKey:];
/**
*/
#ifdef DEBUG
[JSPatch setupDevelopment];
#endif
// -applicationDidBecomeActive:
[JSPatch sync];
5.簡単な代替試験の追加// viewDidLoad
defineClass('RootViewController', {
viewDidLoad: function(){
// ORIG OC :
// self.ORIGviewDidLoad();
self.super().viewDidLoad();
console.log("JS---viewDidLoad---viewDidLoad");
//
require('NSUserDefaults');
// var firstLoad = require('NSUserDefaults').standardUserDefaults();
var firstLoad = NSUserDefaults.standardUserDefaults();
require('NSString');
var saveVersion = NSString.alloc().init();
saveVersion = firstLoad.objectForKey("YENT_Version_Flag");
require('HYUtil');
var currentVersion = HYUtil.getCurrentVersion();
self.labelTitle().setText(" ");
self.setRequestUrl(HYUtil.getServerURL("/main.html#example/index/index"
));
self.tabbar().setSelectedIndex(0);
self.loadRequest();
require('HYString');
if(HYString.isValid(saveVersion))
{
//
var iSaveVersion = saveVersion.stringByReplacingOccurrencesOfString_withString(".","").integerValue();
var iCurrentVersion = currentVersion.stringByReplacingOccurrencesOfString_withString(".","").integerValue();
//
if (iCurrentVersion>iSaveVersion)
{
firstLoad.setObject_forKey(currentVersion,"YENT_Version_Flag");
firstLoad.synchronize();
self.initGuide();
}
else
{
self.initRoot();
}
}
else
{
//
firstLoad.setObject_forKey(currentVersion,"YENT_Version_Flag");
firstLoad.synchronize();
self.initGuide();
}
//
self.showMsg();
},
});
// showMsg
defineClass('RootViewController', {
showMsg:function(){
require('HYUtil').showAlert_message("JSPatch ", "JSPatch ");
},
});