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

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

使用的時候引用 #import <JavaScriptCore/JavaScriptCore.h>

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

          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變量之間的轉換 */
     - (void)jsValue2OCValue{
         
         // 1. 創建javaScript的運行環境
         JSContext *jsC = [[JSContext alloc] init];
         
         // 2. 執行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轉換為OC變量
         int ocv1 = [jsv1 toInt32];
         NSLog(@"%d",ocv1);
         
         // 5. 獲取變量jsv2并轉換
         JSValue *jsv2 = jsC[@"jsv2"];
         NSString *ocv2 = [jsv2 toString];
         NSLog(@"%@",ocv2);
     }
    
  2. JSValue的使用

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

         // 1. 創建js運行環境
         JSContext *jsC = [[JSContext alloc] init];
         
         // 2. 定義block方法供js使用
         jsC[@"log"] = ^(){
             
             NSLog(@"-------Begin Log-------");
             
             // 獲取調用該方法時的參數
             NSArray *args = [JSContext currentArguments];
             for (JSValue *jsVal in args) {
                 id dict = [jsVal toObject];
                 NSLog(@"%@", dict);
             }
             // 獲取調用該方法的對象
             JSValue *this = [JSContext currentThis];
             NSLog(@"this: %@",this);
             
             NSLog(@"-------End Log-------");
         };
         
         // 3. js直接調用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調用JS方法

     /*
      * OC調用JS方法:
      * 1. JS的Func不能轉化成OC的Block:JavaScript方法的參數不固定,Block的參數個數和類型已經返回類型都是固定的
      * 2. 方式1: JSValue的方法-(JSValue *)callWithArguments:(NSArray *)arguments可以運行JS的func
      * 3. 方式2: JSValue的方法- (JSValue *)invokeMethod:(NSString *)method withArguments:(NSArray *)arguments直接簡單地調用對象上的方法。
      * 4. 對于方式2,如果是全局函數,用JSContext的globalObject對象調用;如果是某JavaScript對象上的方法,就應該用相應的JSValue對象調用。
      */
    
     /** OC調用JS方法 */
     - (void)oc_call_js_func{
         
         // 1. 創建js運行環境并執行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調用js的Func
         JSValue *sum1 = [add callWithArguments:@[@(7), @(21)]];
         NSLog(@"Sum1: %d",[sum1 toInt32]);
         
         // 4. 方式2調用js的Func:全局函數
         JSValue *sum2 = [[jsC globalObject] invokeMethod:@"add" withArguments:@[@(1), @(2)]];
         NSLog(@"Sum2: %d",[sum2 toInt32]);
         
     }
    
  5. 異常處理
    /*
    * 異常處理
    * OC異常會在運行時被Xcode捕獲,而在JSContext中執行的JavaScript異常只會被JSContext捕獲并存儲在exception屬性上,不會向外拋出。
    * 如果需要監測JSContext的異常,最合理的方式是給JSContext對象設置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內都不要直接使用其外部定義的JSContext對象或者JSValue,否則會造成循環引用使得內存無法被正確釋放。
      * 應該將其當做參數傳入到Block中,或者通過JSContext的類方法+ (JSContext *)currentContext獲得。
      * 原因:
          1. JSContext會強引用Block
          2. Block引用外部對象時會做強引用
          3. 每個JSValue都會強引用一個JSContext
          4. block引用外部JScontext(箭頭都代表強引用):     JSContext --> Block --> JSContext  循環引用
          5. block引用外部JSValue:           JSValue --> JSContext --> Block --> JSValue  循環引用
      */
    
  7. JSExport的使用
    // 自定義JSExport協議
    @protocol OCPersonExport <JSExport>
    @property (nonatomic, strong) NSDictionary *extMes;
    - (NSString *)fullName;
    - (void)doSometing:(id)something withSomeone:(id)someone;

     // 自定義js調用時的方法名:宏JSExportAs(functionName, ocFunction) 
     JSExportAs(setInfo, - (void)setFirstName:(NSString *)fn lastName:(NSString *)ln desc:(NSDictionary *)desc);
    
     @end
    
     // 遵守協議的類
     @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協議(或子協議)中定義的方法和屬性,都可以在JSContext中被使用

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

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

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

     * 5. 自定義js調用時的方法名:宏JSExportAs(functionName, ocFunction),在js中只要調用方法名functionName,就可以訪問oc對象的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.創建oc類,并傳入js運行環境中
        OCPerson *person = [[OCPerson alloc] init];
        person.firstName = @"O";
        person.lastName = @"C";
        person.extMes = @{@"desc":@"這是OC類1",@"value":@(123)};
        jsC[@"person"] = person;
        
        // 3.js使用oc對象的屬性和方法
        // 3.1 調用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對象里面的值也確實被修改了
        NSLog(@"-----AFTER-----");
        NSLog(@"\n%@", person.extMes);
        
        // 5. 多參數方法調用
        OCPerson *person2 = [[OCPerson alloc] init];
        person2.firstName = @"J";
        person2.lastName = @"S";
        jsC[@"person2"] = person2;
        NSLog(@"---JS調用OC多參數方法---");
        [jsC evaluateScript:@"person.doSometingWithSomeone('talk to', person2);"]; // 獲取屬性
        
        // 6. 自定義js方法名
        // JSExportAs(setInfo, - (void)setFirstName:(NSString *)fn lastName:(NSString *)ln desc:(NSDictionary *)desc);
        // js調用setInfo(),相當于oc調用方法 -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的使用--為已定義的類擴展協議--class_addProtocol

     //定義JSExport子協議
     @protocol JSUITextFieldExport <JSExport>
     @property(nonatomic,copy) NSString *text;
     @end
    
     /** 
      *  為已定義的類擴展協議--class_addProtocol
      *  1. 自定義的OC類,可以繼承自定義的JSExport的協議實現與JavaScript的交互
      *  2. 對于已經定義好的系統類或者外部類,預先不會定義協議提供與JavaScript的交互,OC可以在運行時實時對類拓展協議。
      */
     - (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. 運行時擴展協議
         class_addProtocol([UITextField class], @protocol(JSUITextFieldExport));
         UITextField *textField = [[UITextField alloc]init];
         textField.text = @"0";
         jsC[@"textField"] = textField;
         
         // 3. 獲取擴展協議的屬性,并賦值
         NSLog(@"--為已定義的類擴展協議--");
         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. 內存管理
    /** 內存處理
    * 1. Objective-C是基于引用計數MRC/自動引用計數ARC, javaScript是垃圾回收機制GC, JavaScript對對象的引用為強引用

      *  2. js引用oc變量異常情況:在一個方法中創建了一個臨時的OC對象,將其加入到JSContext后被js使用,js會對該變量進行強引用不會釋放回收(不會增加變量的引用計數器的值),但是OC上的對象可能在方法調用結束后,引用計數變0而被回收內存,此后JavaScript層面容易造成錯誤訪問。
      
      *  3. oc引用js變量異常情況:如果用JSContext創建了對象或者數組,返回JSValue到Objective-C,即使把JSValue變量retain下,但可能因為JavaScript中因為變量沒有了引用而被釋放內存,那么對應的JSValue也沒有用了。
      
      *  4. JSVirtualMachine: JSVirtualMachine就是一個用于保存弱引用對象的數組,加入該數組的弱引用對象因為會被該數組 retain,保證了使用時不會被釋放,當數組里的對象不再需要時,就從數組中移除,沒有了引用的對象就會被系統釋放。
      
      *  5. JSManagedValue:將 JSValue 轉為 JSManagedValue 類型后,可以添加到 JSVirtualMachine 對象中,這樣能夠保證你在使用過程中 JSValue 對象不會被釋放掉,當你不再需要該 JSValue 對象后,從 JSVirtualMachine 中移除該 JSManagedValue 對象,JSValue 對象就會被釋放并置空。)
      */
     - (void)memoryTest{
         
         // 1. 初始化
         JSContext *jsC = [[JSContext alloc] init];
      
         JSValue *value = [[JSValue alloc]init];
         JSManagedValue *managedV = [JSManagedValue managedValueWithValue:value];
         
         // 2. 需要的時候加入virtualMachine中
         [jsC.virtualMachine addManagedReference:managedV withOwner:self];
         
         // 3. 不需要的時候移除
         [jsC.virtualMachine removeManagedReference:managedV withOwner:self];
     }
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,818評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,185評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,656評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,647評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,446評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,951評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,041評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,189評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,718評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,800評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,419評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,420評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,755評論 2 371

推薦閱讀更多精彩內容

  • 本文由我們團隊的 糾結倫 童鞋撰寫。 寫在前面 本篇文章是對我一次組內分享的整理,大部分圖片都是直接從keynot...
    知識小集閱讀 15,276評論 11 172
  • 寫在前面 本篇文章是對我一次組內分享的整理,大部分圖片都是直接從keynote上截圖下來的,本來有很多炫酷動效的,...
    等開會閱讀 14,488評論 6 69
  • 本博客主要分以下幾個方面來介紹iOS中的JavaScriptCore JavaScriptCore簡介 JavaS...
    dullgrass閱讀 4,288評論 1 38
  • 隨著H5技術的興起,在iOS開發過程中,難免會遇到原生應用需要和H5頁面交互的問題。其中會涉及方法調用及參數傳值等...
    Chris_js閱讀 3,099評論 1 8
  • JavaScriptCore框架主要是用來實現iOS與H5的交互。由于現在混合編程越來越多,H5的相對講多,所以研...
    水靈芳蕥閱讀 1,426評論 1 8