iOS+JavaScriptCore.framework的基本使用(一)

項(xiàng)目中涉及OC與網(wǎng)頁的交互,查找資料時(shí)看到了JavaScriptCore.framework,就對(duì)照文章ios7 JavaScriptCore.framework+自己的理解整理了一下,通過注釋進(jìn)行相關(guān)的解釋。
參考:
示例代碼:( YYJavaScriopCoreDemo)
實(shí)際使用:iOS+JavaScriptCore.framework的基本使用(二)

使用的時(shí)候引用 #import <JavaScriptCore/JavaScriptCore.h>

  1. JS與OC變量之間的轉(zhuǎn)換
    /**
    * 1. JSContext:為javaScript提供運(yùn)行環(huán)境
    * 2. JSContext通過-evaluateScript:方法執(zhí)行JavaScript代碼,
    并且代碼中的方法、變量等信息都會(huì)被存儲(chǔ)在JSContext實(shí)例中以便在需要的時(shí)候使用
    * 3. JSValue: OC對(duì)象與JS對(duì)象之間的轉(zhuǎn)換橋梁

          Objective-C type  |   JavaScript type
          --------------------+---------------------
                nil         |     undefined
               NSNull       |        null
              NSString      |       string
              NSNumber      |   number, boolean
            NSDictionary    |   Object object
              NSArray       |    Array object
               NSDate       |     Date object
              NSBlock (1)   |   Function object (1)
                 id (2)     |   Wrapper object (2)
               Class (3)    | Constructor object (3)
      *
      */
    
      /** JS與OC變量之間的轉(zhuǎn)換 */
     - (void)jsValue2OCValue{
         
         // 1. 創(chuàng)建javaScript的運(yùn)行環(huán)境
         JSContext *jsC = [[JSContext alloc] init];
         
         // 2. 執(zhí)行js代碼: - (JSValue *)evaluateScript:(NSString *)script
         NSString *jsCode = @"var jsv1 = 123; var jsv2 = 'jsString' ";
         [jsC evaluateScript:jsCode];
         
         // 3. 通過JSValue獲取js中的變量jsv1
         JSValue *jsv1 = jsC[@"jsv1"];
         
         // 4. 將JSValue轉(zhuǎn)換為OC變量
         int ocv1 = [jsv1 toInt32];
         NSLog(@"%d",ocv1);
         
         // 5. 獲取變量jsv2并轉(zhuǎn)換
         JSValue *jsv2 = jsC[@"jsv2"];
         NSString *ocv2 = [jsv2 toString];
         NSLog(@"%@",ocv2);
     }
    
  2. JSValue的使用

     /**
       * JSValue的使用
       * JSValue通過下標(biāo)可以獲取對(duì)象及對(duì)象的屬性值,也可以通過下標(biāo)直接取值和賦值
       */
     - (void)jsValueProperty{
         
         // 1. 創(chuàng)建js運(yùn)行環(huán)境并執(zhí)行js代碼
         JSContext *jsC = [[JSContext alloc] init];
         NSString *jsCode = @"var jsvArr = [123,'jsString'] ";
         [jsC evaluateScript:jsCode];
         
         // 2. 以下標(biāo)的方式,通過變量名從JSContext中獲取jsvArr
         JSValue *jsvArr = jsC[@"jsvArr"];
         NSLog(@"------Before set-----\n");
         NSLog(@"%@",jsvArr);
         
         // 3. 直接通過下標(biāo)賦值(js無下標(biāo)越位,自動(dòng)延展數(shù)組大小)
         jsvArr[3] = @(YES);
         NSLog(@"------After set-----\n");
         NSLog(@"%@",jsvArr);
    
         // 4. 獲取數(shù)組的屬性值:length
         NSLog(@"------Property-----\n");
         NSLog(@"Array length = %@,%@",jsvArr[@"length"],[jsvArr[@"length"] class]);
     }
    
  3. JS調(diào)用OC方法
    /** JS調(diào)用OC方法
    * 1. 方式:在JSContext中傳入OC的Block當(dāng)做JS的function
    * 2. 獲取JS參數(shù)列表:JSContext的方法,+(JSContext *)currentArguments
    * 3. 獲取調(diào)用該方法的對(duì)象:JSContext的方法,+ (JSValue )currentThis)
    /
    /
    JS調(diào)用OC方法 */
    - (void)js_call_oc_block{

         // 1. 創(chuàng)建js運(yùn)行環(huán)境
         JSContext *jsC = [[JSContext alloc] init];
         
         // 2. 定義block方法供js使用
         jsC[@"log"] = ^(){
             
             NSLog(@"-------Begin Log-------");
             
             // 獲取調(diào)用該方法時(shí)的參數(shù)
             NSArray *args = [JSContext currentArguments];
             for (JSValue *jsVal in args) {
                 id dict = [jsVal toObject];
                 NSLog(@"%@", dict);
             }
             // 獲取調(diào)用該方法的對(duì)象
             JSValue *this = [JSContext currentThis];
             NSLog(@"this: %@",this);
             
             NSLog(@"-------End Log-------");
         };
         
         // 3. js直接調(diào)用oc傳入的block
         NSString *jsCode = @"log('logVal1', [7, 21], { hello:'world', js:100 });";
         [jsC evaluateScript:jsCode];
         
         // 4. js直接使用oc傳入的變量
         jsC[@"newJSValue1"] = @(456);
         [jsC evaluateScript:@"log(newJSValue1)"];
     }
    
  4. OC調(diào)用JS方法

     /*
      * OC調(diào)用JS方法:
      * 1. JS的Func不能轉(zhuǎn)化成OC的Block:JavaScript方法的參數(shù)不固定,Block的參數(shù)個(gè)數(shù)和類型已經(jīng)返回類型都是固定的
      * 2. 方式1: JSValue的方法-(JSValue *)callWithArguments:(NSArray *)arguments可以運(yùn)行JS的func
      * 3. 方式2: JSValue的方法- (JSValue *)invokeMethod:(NSString *)method withArguments:(NSArray *)arguments直接簡(jiǎn)單地調(diào)用對(duì)象上的方法。
      * 4. 對(duì)于方式2,如果是全局函數(shù),用JSContext的globalObject對(duì)象調(diào)用;如果是某JavaScript對(duì)象上的方法,就應(yīng)該用相應(yīng)的JSValue對(duì)象調(diào)用。
      */
    
     /** OC調(diào)用JS方法 */
     - (void)oc_call_js_func{
         
         // 1. 創(chuàng)建js運(yùn)行環(huán)境并執(zhí)行js代碼
         JSContext *jsC = [[JSContext alloc] init];
         NSString *jsCode = @"function add(a, b) { return a + b; }";
         [jsC evaluateScript:jsCode];
         
         // 2. 獲取方法
         JSValue *add = jsC[@"add"];
         NSLog(@"Func: %@", add);
         
         // 3. 方式1調(diào)用js的Func
         JSValue *sum1 = [add callWithArguments:@[@(7), @(21)]];
         NSLog(@"Sum1: %d",[sum1 toInt32]);
         
         // 4. 方式2調(diào)用js的Func:全局函數(shù)
         JSValue *sum2 = [[jsC globalObject] invokeMethod:@"add" withArguments:@[@(1), @(2)]];
         NSLog(@"Sum2: %d",[sum2 toInt32]);
         
     }
    
  5. 異常處理
    /*
    * 異常處理
    * OC異常會(huì)在運(yùn)行時(shí)被Xcode捕獲,而在JSContext中執(zhí)行的JavaScript異常只會(huì)被JSContext捕獲并存儲(chǔ)在exception屬性上,不會(huì)向外拋出。
    * 如果需要監(jiān)測(cè)JSContext的異常,最合理的方式是給JSContext對(duì)象設(shè)置exceptionHandler
    */

     - (void)jsExceptionHandler{
         
         JSContext *context = [[JSContext alloc] init];
         context.exceptionHandler = ^(JSContext *con, JSValue *exception) {
             NSLog(@"%@", exception);
             
             //別忘了給exception賦值,否則JSContext的異常信息為空
             con.exception = exception;
         };
         
         [context evaluateScript:@"arrar[0] = 21"];
     }
    
  6. Block使用注意

     /*
      * 在Block內(nèi)都不要直接使用其外部定義的JSContext對(duì)象或者JSValue,否則會(huì)造成循環(huán)引用使得內(nèi)存無法被正確釋放。
      * 應(yīng)該將其當(dāng)做參數(shù)傳入到Block中,或者通過JSContext的類方法+ (JSContext *)currentContext獲得。
      * 原因:
          1. JSContext會(huì)強(qiáng)引用Block
          2. Block引用外部對(duì)象時(shí)會(huì)做強(qiáng)引用
          3. 每個(gè)JSValue都會(huì)強(qiáng)引用一個(gè)JSContext
          4. block引用外部JScontext(箭頭都代表強(qiáng)引用):     JSContext --> Block --> JSContext  循環(huán)引用
          5. block引用外部JSValue:           JSValue --> JSContext --> Block --> JSValue  循環(huán)引用
      */
    
  7. JSExport的使用
    // 自定義JSExport協(xié)議
    @protocol OCPersonExport <JSExport>
    @property (nonatomic, strong) NSDictionary *extMes;
    - (NSString *)fullName;
    - (void)doSometing:(id)something withSomeone:(id)someone;

     // 自定義js調(diào)用時(shí)的方法名:宏JSExportAs(functionName, ocFunction) 
     JSExportAs(setInfo, - (void)setFirstName:(NSString *)fn lastName:(NSString *)ln desc:(NSDictionary *)desc);
    
     @end
    
     // 遵守協(xié)議的類
     @interface OCPerson : NSObject<OCPersonExport>
     @property (nonatomic, copy) NSString *firstName;
     @property (nonatomic, copy) NSString *lastName;
     @end
    
     @implementation OCPerson
     @synthesize extMes;
     - (NSString *)fullName {
         return [NSString stringWithFormat:@"%@·%@", self.firstName, self.lastName];
     }
     - (void)doSometing:(id)something withSomeone:(OCPerson *)someone{
         NSLog(@"%@ %@ %@",self.fullName,something,someone.fullName);
     }
     - (void)setFirstName:(NSString *)fn lastName:(NSString *)ln desc:(NSDictionary *)desc{
         self.firstName = fn;
         self.lastName = ln;
         self.extMes = desc;
     }
     @end
    

.
/*
* 1. 所有JSExport協(xié)議(或子協(xié)議)中定義的方法和屬性,都可以在JSContext中被使用

     * 2. 遵守了JSExport協(xié)議(或子協(xié)議)的類,額外定義的方法/屬性,JSContext訪問不到

     * 3. JSExport可以正確反映屬性聲明,例如readonly的屬性,在JavaScript中也就只能讀取值而不能賦值。

     * 4. 對(duì)于多參數(shù)的方法,為能被js使用,JavaScriptCore會(huì)對(duì)其進(jìn)行轉(zhuǎn)換。
     *      4.1 轉(zhuǎn)換方式:將OC方法冒號(hào)后的字母大寫,并移除冒號(hào), 參數(shù)移到后面。
     *      4.2 例如:方法- (void)doSometing:(id)something withSomeone:(id)someone;
     *          在JavaScript調(diào)用就是:doSometingWithSomeone(something, someone);

     * 5. 自定義js調(diào)用時(shí)的方法名:宏JSExportAs(functionName, ocFunction),在js中只要調(diào)用方法名functionName,就可以訪問oc對(duì)象的ocFunction方法
     */

    /** JSExport的使用 */
    - (void)jsExportTest{
        
        // 1.初始化
        JSContext *jsC = [[JSContext alloc] init];
        jsC[@"log"] = ^(){
            NSArray *args = [JSContext currentArguments];
            for (JSValue *jsVal in args) {
                NSLog(@"%@", jsVal);
            }
        };
        jsC.exceptionHandler = ^(JSContext *con, JSValue *exception) {
            NSLog(@"%@", exception);
            con.exception = exception;
        };
        
        // 2.創(chuàng)建oc類,并傳入js運(yùn)行環(huán)境中
        OCPerson *person = [[OCPerson alloc] init];
        person.firstName = @"O";
        person.lastName = @"C";
        person.extMes = @{@"desc":@"這是OC類1",@"value":@(123)};
        jsC[@"person"] = person;
        
        // 3.js使用oc對(duì)象的屬性和方法
        // 3.1 調(diào)用OCPersonExport中的方法 -(NSString *)fullName;
        [jsC evaluateScript:@"log('-----fullName-----',person.fullName());"];
        
        // 3.2 未獲取到,OCPersonExport沒有定義該屬性/方法
        [jsC evaluateScript:@"log('-----firstName-----',person.firstName);"];
        
        // 3.3 未獲取到,OCPersonExport沒有定義該屬性/方法
        [jsC evaluateScript:@"log('-----lastName-----',person.lastName);"];
        
        // 3.4 獲取OCPersonExport的屬性值extMes;
        [jsC evaluateScript:@"log('-----extMes-----','desc:', person.extMes.desc, 'value:', person.extMes.value);"];
        
        // 3.5 修改OCPersonExport的屬性值extMes;
        [jsC evaluateScript:@"person.extMes = {desc:'被js修改后的OC類1'}"];
        
        // 3.6 獲取屬性extMes
        [jsC evaluateScript:@"log('-----extMes-----','desc:', person.extMes.desc, 'value:', person.extMes.value);"];
        
        // 4. oc對(duì)象里面的值也確實(shí)被修改了
        NSLog(@"-----AFTER-----");
        NSLog(@"\n%@", person.extMes);
        
        // 5. 多參數(shù)方法調(diào)用
        OCPerson *person2 = [[OCPerson alloc] init];
        person2.firstName = @"J";
        person2.lastName = @"S";
        jsC[@"person2"] = person2;
        NSLog(@"---JS調(diào)用OC多參數(shù)方法---");
        [jsC evaluateScript:@"person.doSometingWithSomeone('talk to', person2);"]; // 獲取屬性
        
        // 6. 自定義js方法名
        // JSExportAs(setInfo, - (void)setFirstName:(NSString *)fn lastName:(NSString *)ln desc:(NSDictionary *)desc);
        // js調(diào)用setInfo(),相當(dāng)于oc調(diào)用方法 -setFirstName:lastName:desc:
        NSLog(@"---定義js方法名---");
        [jsC evaluateScript:@"person2.setInfo('JS', 'New', {desc: ' call custom functionName'});"];
        [jsC evaluateScript:@"var str = person2.fullName() + person2.extMes.desc; log(str);"];
    }
  1. JSExport的使用--為已定義的類擴(kuò)展協(xié)議--class_addProtocol

     //定義JSExport子協(xié)議
     @protocol JSUITextFieldExport <JSExport>
     @property(nonatomic,copy) NSString *text;
     @end
    
     /** 
      *  為已定義的類擴(kuò)展協(xié)議--class_addProtocol
      *  1. 自定義的OC類,可以繼承自定義的JSExport的協(xié)議實(shí)現(xiàn)與JavaScript的交互
      *  2. 對(duì)于已經(jīng)定義好的系統(tǒng)類或者外部類,預(yù)先不會(huì)定義協(xié)議提供與JavaScript的交互,OC可以在運(yùn)行時(shí)實(shí)時(shí)對(duì)類拓展協(xié)議。
      */
     - (void)extendJSExportProtocal{
         
         // 1. 初始化
         JSContext *jsC = [[JSContext alloc] init];
         jsC[@"log"] = ^(){
             NSArray *args = [JSContext currentArguments];
             for (JSValue *jsVal in args) {
                 NSLog(@"%@", jsVal);
             }
         };
         jsC.exceptionHandler = ^(JSContext *con, JSValue *exception) {
             NSLog(@"%@", exception);
             con.exception = exception;
         };
         
         // 2. 運(yùn)行時(shí)擴(kuò)展協(xié)議
         class_addProtocol([UITextField class], @protocol(JSUITextFieldExport));
         UITextField *textField = [[UITextField alloc]init];
         textField.text = @"0";
         jsC[@"textField"] = textField;
         
         // 3. 獲取擴(kuò)展協(xié)議的屬性,并賦值
         NSLog(@"--為已定義的類擴(kuò)展協(xié)議--");
         NSString *script = @"var num = parseInt(textField.text, 10);"http:// js獲取屬性值
         "textField.text = 10;"                                       // js賦值
         "var string = 'oldText:'+num+' newText:'+textField.text;"
         "log(string)";
         [jsC evaluateScript:script];
     }
    
  2. 內(nèi)存管理
    /** 內(nèi)存處理
    * 1. Objective-C是基于引用計(jì)數(shù)MRC/自動(dòng)引用計(jì)數(shù)ARC, javaScript是垃圾回收機(jī)制GC, JavaScript對(duì)對(duì)象的引用為強(qiáng)引用

      *  2. js引用oc變量異常情況:在一個(gè)方法中創(chuàng)建了一個(gè)臨時(shí)的OC對(duì)象,將其加入到JSContext后被js使用,js會(huì)對(duì)該變量進(jìn)行強(qiáng)引用不會(huì)釋放回收(不會(huì)增加變量的引用計(jì)數(shù)器的值),但是OC上的對(duì)象可能在方法調(diào)用結(jié)束后,引用計(jì)數(shù)變0而被回收內(nèi)存,此后JavaScript層面容易造成錯(cuò)誤訪問。
      
      *  3. oc引用js變量異常情況:如果用JSContext創(chuàng)建了對(duì)象或者數(shù)組,返回JSValue到Objective-C,即使把JSValue變量retain下,但可能因?yàn)镴avaScript中因?yàn)樽兞繘]有了引用而被釋放內(nèi)存,那么對(duì)應(yīng)的JSValue也沒有用了。
      
      *  4. JSVirtualMachine: JSVirtualMachine就是一個(gè)用于保存弱引用對(duì)象的數(shù)組,加入該數(shù)組的弱引用對(duì)象因?yàn)闀?huì)被該數(shù)組 retain,保證了使用時(shí)不會(huì)被釋放,當(dāng)數(shù)組里的對(duì)象不再需要時(shí),就從數(shù)組中移除,沒有了引用的對(duì)象就會(huì)被系統(tǒng)釋放。
      
      *  5. JSManagedValue:將 JSValue 轉(zhuǎn)為 JSManagedValue 類型后,可以添加到 JSVirtualMachine 對(duì)象中,這樣能夠保證你在使用過程中 JSValue 對(duì)象不會(huì)被釋放掉,當(dāng)你不再需要該 JSValue 對(duì)象后,從 JSVirtualMachine 中移除該 JSManagedValue 對(duì)象,JSValue 對(duì)象就會(huì)被釋放并置空。)
      */
     - (void)memoryTest{
         
         // 1. 初始化
         JSContext *jsC = [[JSContext alloc] init];
      
         JSValue *value = [[JSValue alloc]init];
         JSManagedValue *managedV = [JSManagedValue managedValueWithValue:value];
         
         // 2. 需要的時(shí)候加入virtualMachine中
         [jsC.virtualMachine addManagedReference:managedV withOwner:self];
         
         // 3. 不需要的時(shí)候移除
         [jsC.virtualMachine removeManagedReference:managedV withOwner:self];
     }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 本文由我們團(tuán)隊(duì)的 糾結(jié)倫 童鞋撰寫。 寫在前面 本篇文章是對(duì)我一次組內(nèi)分享的整理,大部分圖片都是直接從keynot...
    知識(shí)小集閱讀 15,385評(píng)論 11 172
  • 寫在前面 本篇文章是對(duì)我一次組內(nèi)分享的整理,大部分圖片都是直接從keynote上截圖下來的,本來有很多炫酷動(dòng)效的,...
    等開會(huì)閱讀 14,720評(píng)論 6 69
  • 本博客主要分以下幾個(gè)方面來介紹iOS中的JavaScriptCore JavaScriptCore簡(jiǎn)介 JavaS...
    dullgrass閱讀 4,422評(píng)論 1 38
  • 隨著H5技術(shù)的興起,在iOS開發(fā)過程中,難免會(huì)遇到原生應(yīng)用需要和H5頁面交互的問題。其中會(huì)涉及方法調(diào)用及參數(shù)傳值等...
    Chris_js閱讀 3,237評(píng)論 1 8
  • JavaScriptCore框架主要是用來實(shí)現(xiàn)iOS與H5的交互。由于現(xiàn)在混合編程越來越多,H5的相對(duì)講多,所以研...
    水靈芳蕥閱讀 1,500評(píng)論 1 8

友情鏈接更多精彩內(nèi)容