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);"// 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阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容

  • 本文由我们团队的 纠结伦 童鞋撰写。 写在前面 本篇文章是对我一次组内分享的整理,大部分图片都是直接从keynot...
    知识小集阅读 15,220评论 11 172
  • 写在前面 本篇文章是对我一次组内分享的整理,大部分图片都是直接从keynote上截图下来的,本来有很多炫酷动效的,...
    等开会阅读 14,384评论 6 69
  • 本博客主要分以下几个方面来介绍iOS中的JavaScriptCore JavaScriptCore简介 JavaS...
    dullgrass阅读 4,263评论 1 38
  • 随着H5技术的兴起,在iOS开发过程中,难免会遇到原生应用需要和H5页面交互的问题。其中会涉及方法调用及参数传值等...
    Chris_js阅读 3,053评论 1 8
  • JavaScriptCore框架主要是用来实现iOS与H5的交互。由于现在混合编程越来越多,H5的相对讲多,所以研...
    水灵芳蕥阅读 1,382评论 1 8