iOS Hot update-jspatch Implementation principle +patch onsite Recovery

Source: Internet
Author: User
Tags lua

About Hotfixpatch

In the area of iOS development, because of Apple's stringent auditing standards and inefficiencies, iOS apps are extremely slow to release, with a slightly larger version of the app almost one months old, so code hot Update (Hotfixpatch) is especially important for iOS apps.

Now the industry is basically using the Waxpatch scheme, because the wax framework has been discontinued for four or five years, so waxpatch in the use of the process there are still many pits (such as the problem of parameter conversion process, if the inheriting class is not instantiated to modify the method of inheriting class is invalid, Wax_ Delayed release of instance held in the GC for OC ...). In addition, Apple's attitude to the use of wax is also in a vague state, which is also a potential use of risk.

With the Facebook open source react native framework, using javascriptcore.framework to directly build bridge between JavaScript (JS) and Objective-c (OC) is possible, Jspatch was born at this time. The first is from the Tang Qiao public number push to understand, began to think is react native on the basis of the package, but recently carefully studied the source code, with react native half a dime relationship is not, here first to the author of Jspatch (not Tang Qiao, is bang, Blog address) likes one.

After an in-depth look at Jspatch, the first feeling is that the scheme is small, easy to understand, low maintenance, and directly through the OC code to call the runtime API, as an iOS developer, will soon be able to understand, do not have to spend a lot of effort to understand learning Lua. In addition, in the establishment of JS and OC Bridge, the author very clever use of JS and OC Two language message forwarding mechanism to do a very elegant implementation, slightly less is jspatch can only support iOS7 and above.

Since some of the company's applications are still supporting IOS6, completely replace the wax is not realistic, but some new applications have directly started to support iOS7. Personally think IOS6 and iOS7 interface style difference is large, I believe that the application of the minimum support version will be upgraded to iOS7 soon. Also taking into account the Jspatch maturity is not enough, so decided to combine Jspatch and waxpatch, complementary to use. Below to tell you some of the experience of learning to use.

Comparison of Jspatch and Waxpatch

For the advantages of Jspatch vs Waxpatch, take a look at the Jspatch author's words:

    • Source: jspatch– Dynamic update iOS APP
Scenario Comparison

There are a number of scenarios where dynamic patching can be implemented, such as Waxpatch, which can be used with LUA to invoke the OC method, relative to the Waxpatch,jspatch advantage:

    • 1.js language: JS than Lua in the field of application development has a broader application, the current front-end development and terminal development has a convergence trend, as an extension of the scripting language, JS is the perfect choice.

    • 2. comply with Apple rules: Jspatch is more consistent with Apple's rules. IOS Developer Program License Agreement 3.3.2 mentions that executable code cannot be issued dynamically, except for code executed by Apple Javascriptcore.framework or WebKit, JS is executed by Javascriptcore.framework.

    • 3. Compact: using the system built-in javascriptcore.framework, no embedded scripting engine, small size.

    • 4. Support BLOCK: Wax stopped development and maintenance several years ago, does not support the OBJECTIVE-C block and LUA program, although some third parties have implemented block, but the use of parameters there are more restrictions.

Disadvantages of Jspatch:

    • The disadvantage with respect to Waxpatch,jspatch is that iOS6 is not supported because Javascriptcore.framework is required. In addition, the current memory usage will be higher than wax, continuous improvement.
Understanding of the realization principle of Jspatch

Jspatch the implementation of the principle of the author's blog has been very detailed introduction, I do not say more here, paste the learning place:

    • Jspatch Implementation principle Detailed http://blog.cnbang.net/tech/2808/
    • Jspatch git source code and usage instructions Https://github.com/bang590/JSPatch

Look at the implementation of the principle of detail when the control of the source to see, a better understanding, I am here to say my study and understanding of Jspatch:

(1) Dynamic language characteristics of OC

Regardless of the Waxpatch framework or the Jspatch scheme, the fundamental principle is to make use of the dynamic language characteristics of OC to dynamically modify the method of the class.
The dynamic language features of OC are implemented on the runtime system (all implemented in C, Apple maintains an open source code), and the object-oriented class and instance mechanisms are based on the message mechanism. What we usually think of as [object method], the correct understanding should be [receiver sendmsg], all the message sent in the compilation phase compiled into the runtime C function call: _obj_sendmsg (ID, SEL).

For more information, refer to the blog:

    • OBJECTIVE-C Runtime Detailed Introduction
    • OBJECTIVE-C Runtime Source _apple

Runtime provides some API to run time

    • Reflection classes and selectors
    NSClassFromString("UIViewController");    SEL selector = NSSelectorFromString("viewDidLoad");
    • Add or replace the implementation of the method selector (SEL) for a class (IMP)
    BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types); IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types);
    • Dynamically registering classes in runtime
    Class superCls = NSClassFromString(superClassName);    cls = objc_allocateClassPair(superCls, className.UTF8String, 0); objc_registerClassPair(cls);
(2) JS how to call OC

In the JS runtime environment, there are two problems to be solved, one is the acquisition of the OC Class object (Objc_class), and the other is to use the interface method provided by the object.

For the first problem, jspatch in the implementation is through the require call in the JS environment to create a class with the same name of the object (JS form), when sent to OC Alloc receive the message, will be created in the OC environment to save the object address to this JS object in the same name, JS itself does not complete the initialization of any object. The reference to JS holding OC objects is explained in the Jspatch author's blog post, with no specific tests. See Jspatch.js Code:

    Request OC Class objectUIView = require ("UIView");Cache JS class object with the same namevar _require = function (clsname) {if (!global[clsname]) {Global[clsname] = {__iscls:1, __clsname:clsname}}Return Global[clsname]}Call the class method to return the OC instantiation object for encapsulationvar ret = instance? _oc_calli (instance, Selectorname, args, Issuper): _OC_CALLC (Clsname, Selectorname, args) //oc after creation return object [email protected]{@< Span class= "hljs-string" > "__clsname": nsstringfromclass ([obj < Span class= "Hljs-keyword" >class]), @ "__obj": obj}; //js in the resolution OC object return _formatoctojs (ret) //_ Formatoctojs if (obj instanceof object) { var ret = {} for (var key in obj) {ret [Key] = _formatoctojs (Obj[key])} return ret}       /span>           

For the second problem, jspatch in the JS environment through the central forwarding method, all OC method calls through the new object (JS) prototype method _c (MethodName) to complete the call, before the JS script through JavaScriptCore execution, Replace All method call characters first
_c (' method ') method, in _c function through the Jscontex established bridge function passed in the parameters and the return parameter is completed the call;

    Character substitutionStaticNSString *_regexstr =@ "\\.\\s* (\\w+) \\s*\\ (";StaticNSString *_replacestr =@ ". __c (\" $1\ ") (";NSString *formatedscript = [NSString stringWithFormat:@ "Try{@}catch (e) {_oc_catch (E.message, E.stack)}", [_regex stringbyreplacingmatchesinstring:script options:0 Range:Nsmakerange (0, Script. length) withtemplate:_replacestr]];__c () forwards the call parameter to OC Object. prototype. __c = function (methodName) {...return function () {var args = Array. prototype. Slice. Call (arguments)Return _methodfunc (Self. __obj,Self. __clsname, MethodName, args,Self. __issuper)}}_methodfunc Call bridge function var _methodfunc = function (instance, Clsname, MethodName, args, issuper) {... var ret = instance? _o C_calli (instance, Selectorname, args, Issuper): _OC_CALLC (Clsname, selectorname, args)return _formatoctojs (ret)} bridge functions in//oc, JS and OC bridge functions are defined by this context[@ "_oc_calli"] = ^ID (jsvalue *obj, nsstring *selectorname, Jsvalue *arguments, BOOL issuper) { return callselector (nil, selectorname, arguments, obj, issuper);}; Contex t[@ "_OC_CALLC"] = ^ID (nsstring *classname, nsstring *selectorname, Jsvalue *arguments) { return Callselector (ClassName, Selectorname, arguments, nil, NO)         ; 
(3) JS How to replace OC method

The main function of Jspatch is to fix some online bugs through a script, hoping to achieve the goal of replacing the OC method. The clever thing about Jspatch is that it utilizes the message forwarding mechanism of OC.

    • 1: Replace the original selector IMP implementation for an empty IMP implementation, so that when the Objc_class received the message, the message will be forwarded, in addition to the initial implementation of selector to save;
 //selector point to NULL implementation IMP Msgforwardimp = Getemptymsgforwardimp (type Description, methodsignature); Class_replacemethod (CLS, selector, Msgforwardimp, typedescription); //Save the original implementation, modified here, added support for recovery site nsstring * Originalselectorname = [nsstring stringwithformat:@ "[email  protected] ", selectorname]; SEL originalselector = nsselectorfromstring (originalselectorname); if (Class_respondstoselector (CLS, selector)) {if (!class_ Respondstoselector (CLS, originalselector)) {Class_addmethod (CLS, Originalselector, Originalimp, typeDescription); Span class= "Hljs-keyword" >else {class_replacemethod (CLS, Originalselector, Originalimp, typedescription);}}  
    • 2: Constructs a jpselector and its IMP implementation (constructed according to the return parameters) of the replacement JS method, adds it to the current class, and through the CLS+SELECOTR global cache JS method (the global cache does not have much use, However, it is useful for recovering the site later);
 if (!_jsoveridemethods[clsname][        Jpselectorname]) {_initjpoveridemethods (clsname);        _jsoveridemethods[clsname][jpselectorname] = function;        const char *returntype = [Methodsignature methodreturntype]; IMP jpimplementation = null; //constructs switch (Returntype[0 by return type) ){ ... } if (!class_respondstoselector (CLS, jpselector)) {Class_addmethod (CLS, JPSelector, Jpimplementation, typedescription); } else {class_replacemethod (CLS, Jpselector, jpimplementation,typedescription);}}  
    • 3: Then rewrite the forwadinvocation implementation of each substitution method class to intercept, if the intercepted invocation Selctor converted to Jpselector can respond, the description is a replacement method, The imp of Jpselector is called after taking the parameter from invocation;
    Staticvoid Jpforwardinvocation (ID SLF, SEL selector,Nsinvocation *invocation) {Nsmethodsignature *methodsignature = [invocation methodsignature];Nsinteger numberofarguments = [Methodsignature numberofarguments];NSString *selectorname =Nsstringfromselector (invocation. selector);NSString *jpselectorname = [NSString stringWithFormat:@ "[email protected]", selectorname]; SEL jpselector = nsselectorfromstring (jpselectorname); if (!class_respondstoselector (Object_getclass (SLF), jpselector)) {...} Nsmutablearray *arglist = [[Nsmutablearray alloc] init]; [ArgList ADDOBJECT:SLF]; For (nsuinteger i = 2; i < numberofarguments; i++) {...} after getting the parameters, invoke Jpsector calls the implementation of Jsfunction @synchronized (_context) {_tmpinvocationarguments = Formatoctojslist (argList); [Invocation setselector:jpselector]; [Invocation invoke]; _tmpinvocationarguments = nil;}}            
Patch Site Recovery Supplement

Patch site recovery features are primarily used to continuously update scripts in the application scenario. Because iOS app apps press the home key or the phone is interrupted, the app is actually first into the background runtime (applicationwillresignactive), and when we use the app again next time, If the background app is not terminated (applicationwillterminate), then the app does not go Appliation:didfinishlaunchingwithoptions method, But will Go (applicationwillenterforeground). For this scenario, if we update the online script continuously, then the second script update cannot retain the first method implementation, and the recovery of the field function will also help us to undo the ability of the online script to restore its own code.

On-site recovery of Jspatch

In this paper, on the basis of Jspatch added on-site recovery function; Source Address reference:

    • Jspatchdemo to increase site recovery:
      Https://github.com/philonpang/JSPatch.git

The description is as follows:

(1) Added two start and end call functions in JPEngine.h as follows:

    void js_start(NSString* initScript);    void js_end();

(2) The implementation of the calling function in JPENGINE.M and the modification of the partial code in the recovery field: mainly using the replacement method and the new method of the cache (_jsoveridemethods, mainly this)

    To handle the replacement method, selector refers back to the original Imp,jpselector and Origselector both point to the non-implemented IMPif ([Jpselectorname hasprefix:@ "_JP"]) {if (Class_getmethodimplementation (CLS,@selector (forwardinvocation:)) = = (IMP) jpforwardinvocation) {SEL Origforwardselector =@selector (origforwardinvocation:); IMP origforwardimp = class_getmethodimplementation (CLS, origforwardselector); Class_replacemethod (CLS,@selector (forwardinvocation:), Origforwardimp,"[Email protected]:@"); Class_replacemethod (CLS, Origforwardselector, _objc_msgforward,"[Email protected]:@"); }NSString *selectorname = [Jpselectorname stringbyreplacingoccurrencesofstring:@ "_JP" withstring:@""];NSString *origselectorname = [Jpselectorname stringbyreplacingoccurrencesofstring:@ "_JP" withstring:@ "ORIG"]; SEL Jpselector =Nsselectorfromstring (Jpselectorname); SEL selector =Nsselectorfromstring (Selectorname); SEL Origselector =Nsselectorfromstring (Origselectorname);if (Class_respondstoselector (CLS, origselector) && class_respondstoselector (CLS, selector) && Class_ Respondstoselector (CLS, jpselector)) {Nsmethodsignature *methodsignature = [CLS Instancemethodsignatureforselector:origselector]; method = Class_getinstancemethod (CLS, origselector);Char *typedescription = (char *) method_gettypeencoding (method); IMP forwardemptyimp = Getemptymsgforwardimp (typedescription, methodsignature); IMP origselectorimp = class_getmethodimplementation (CLS, origselector); Class_replacemethod (CLS, selector, Origselectorimp, typedescription); Class_replacemethod (CLS, Jpselector, Forwardemptyimp, typedescription); Class_replacemethod (CLS, Origselector, Forwardemptyimp, typedescription); } }//Handling new method of adding else {isclsnew = YES; SEL jpselector = nsselectorfromstring (jpselectorname); if (Class_respondstoselector (CLS, jpselector)) { nsmethodsignature *methodsignature = [CLS Instancemethodsignatureforselector:jpselector]; method = Class_getinstancemethod (CLS, jpselector); char *typedescription = (char *) method_gettypeencoding (method); IMP forwardemptyimp = Getemptymsgforwardimp (typedescription, methodsignature); Class_replacemethod (CLS, Jpselector, Forwardemptyimp, typedescription); } }
Those pits in Hotfixpatch.

Waxpatch before some colleagues complained that there are a lot of pits, jspatch in the use of the process will encounter a lot of pits, so although both frameworks now can be done to add executable code, but it is not advisable to apply it to the development of functional components.

For example, I met a hole in my first use of Jspatch: (I want to write a blog post to collect some of the pits our team has encountered using patches ~ ~)

    • When the JS script overwrites the Optional Protocol method of an inherited class that is not implemented in the derived class, the JS patch method is not invoked TableView reload, but the TableView method that can invoke the substitution is explicitly called in selector In addition, if the protocol method is overridden in a derived class, it can be adjusted;

iOS Hot update-jspatch Implementation principle +patch onsite Recovery

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.