React Native源码中JavaScriptCore详解

作者: 袁峥 | 来源:发表于2017-03-29 01:31 被阅读5702次

    前言

    最近在看React Native源码的时候,发现内部使用了一个JavaScriptCore框架,可以用来进行OC和JS互相调用,借机写了一篇OC和JS互相调用,为后续想了解React Native源码的同学埋下伏笔,当然后期也会把自己所了解的React Native源码实现跟大家分享。

    如果喜欢我的文章,可以关注我微博:袁峥Seemygo

    一、JavaScriptCore常用的类

    •  JavaScriptCore作用:JavaScriptCore是苹果原生API,用来JS和OC交互的。
      
    • JSContext: JS运行环境,用它去执行JS代码,并且通过它去获取JS里的数据
    • JSValue: 用于接收JS中获取的数据类型,可以是任一对象,方法。

    二、OC调用JS

    • 本质:JS代码中已经定义好变量和方法,通过OC去获取,并且调用
    • 步骤:
      • 1.创建JS运行环境
      • 2.执行JS代码
      • 3.获取JS数据(变量,方法)
      • 4.使用JS数据,方法

    2.1 获取定义在JS中的变量

    • 可以直接通过OC修改JS中变量的值
    #pragma mark - 获取JS中定义的变量
    - (void)getJSVar
    {
        // JS代码
        NSString *jsCode = @"var arr = [1,2,3]";
        
        // 创建JS运行环境
        JSContext *ctx = [[JSContext alloc] init];
        
        // 执行JS代码
        [ctx evaluateScript:jsCode];
        
        // 因为变量直接定义在JS中,所以可以直接通过JSContext获取,根据变量名称获取,相当于字典的Key
        // 只有先执行JS代码,才能获取变量
        JSValue *jsArr = ctx[@"arr"];
        
        jsArr[0] = @5;
        
        // 打印结果:5,2,3
        NSLog(@"%@",jsArr);
    }
    
    

    2.2 获取定义在JS中的方法,并且调用

    • 实现OC调用JS方法
    #pragma mark - OC调用JS
    // OC调用JS方法,并获取返回结果
    - (void)ocCallJSFunc
    {
        NSString *jsCode = @"function hello(say){"
        " return say; "
        "}";
        
        // 创建JS运行环境
        JSContext *ctx = [[JSContext alloc] init];
        
        // 因为方法直接定义在JS中,所以可以直接通过JSContext获取,根据方法名称获取,相当于字典的Key
        // 执行JS代码
        [ctx evaluateScript:jsCode];
        
        // 获取JS方法,只有先执行JS代码,才能获取
        JSValue *hello = ctx[@"hello"];
        
        // OC调用JS方法,获取方法返回值
        JSValue *result = [hello callWithArguments:@[@"你好"]];
        
        // 打印结果:你好
        NSLog(@"%@",result);
    }
    

    三、JS调用OC中的block

    • 本质:一开始JS中并没有OC的block,所以没法直接调用OC的block,需要把OC的block,在JS中生成方法,然后在通过JS调用。
    • 步骤:
      • 1.创建JS运行环境
      • 2.在JS中生成对应的OC代码
      • 3.使用JS调用,在JS环境中生成的block方法,就能调用到OC的block中.

    3.1 JS调用OC中不带参数的block

    • 想通过JS调用OC中不带参数的block
    #pragma mark - JS调用OC中不带参数的block
    - (void)jsCallOCBlock1WithNoneArguments
    {
        // 创建JS运行环境
        JSContext *ctx = [[JSContext alloc] init];
        
        // JS调用Block方式
        // 由于JS本身没有OC这个代码,需要给JS中赋值,就会自动生成右边的代码.
        // 相当于在JS中定义一个叫eat的方法,eat的实现就是block中的实现,只要调用eat,就会调用block
        ctx[@"eat"] = ^(){
            NSLog(@"吃东西");
        };
        
        // JS执行代码,就会直接调用到block中
        NSString *jsCode = @"eat()";
        
        [ctx evaluateScript:jsCode];
    }
    
    

    3.2 JS调用OC中带参数的block

    • 想通过JS调用OC中带参数的block
    - (void)jsCallOCBlockWithArguments
    {
        // 创建JS运行环境
        JSContext *ctx = [[JSContext alloc] init];
        
        // 2.调用带有参数的block
        // 还是一样的写法,会在JS中生成eat方法,只不过通过[JSContext currentArguments]获取JS执行方法时的参数
        ctx[@"eat"] = ^(){
            // 获取JS调用参数
            NSArray *arguments = [JSContext currentArguments];
            NSLog(@"吃%@",arguments[0]);
        };
        
        // JS执行代码,调用eat方法,并传入参数面包
        NSString *jsCode = @"eat('面包')";
        
        [ctx evaluateScript:jsCode];
    
    }
    
    

    四、JS调用OC中的类

    • 本质:一开始JS中并没有OC的类,需要先在JS中生成OC的类,然后在通过JS调用。
    • 步骤
      • 1.OC类必须遵守JSExport协议,只要遵守JSExport协议,JS才会生成这个类
      • 2.但是还不够,类里面有属性和方法,也要在JS中生成
      • 3.JSExport本身不自带属性和方法,需要自定义一个协议,继承JSExport,在自己的协议中暴露需要在JS中用到的属性和方法
      • 4.这样自己的类只要继承自己的协议就好,JS就会自动生成类,包括自己协议中声明的属性和方法

    4.1 JS调用OC自定义类

    • 自定义协议(PersonJSExport)
    @protocol PersonJSExport <JSExport>
    
    @property (nonatomic, strong) NSString *name;
    
    - (void)play;
    
    // 调用多个参数的方法,JS函数命名规则和OC还不一样,很可能调用不到对应的JS生成的函数,为了保证生成的JS函数和OC方法名一致,OC提供了一个宏JSExportAs,用来告诉JS应该生成什么样的函数对应OC的方法,这样就不会调错了。
    
    // PropertyName:JS函数生成的名字
    // Selector:OC方法名
    // JS就会自动生成playGame这个方法
    JSExportAs(playGame, - (void)playWithGame:(NSString *)game time:(NSString *)time);
    
    @end
    
    
    • 自定义类(Person)
    @interface Person : NSObject <PersonJSExport>
    
    @property (nonatomic, strong) NSString *name;
    
    
    - (void)playWithGame:(NSString *)game time:(NSString *)time;
    
    @end
    
    
    @implementation Person
    
    - (void)play
    {
        
        NSLog(@"%@玩",_name);
    }
    
    - (void)playWithGame:(NSString *)game time:(NSString *)time
    {
        NSLog(@"%@在%@玩%@",_name,time,game);
    }
    
    
    @end
    
    
    • 通过JS调用OC自定义类
    
    
    #pragma mark - JS调用OC自定义类
    - (void)jsCallOCCustomClass
    {
        // 创建Person对象
        Person *p = [[Person alloc] init];
        p.name = @"yz";
        
        JSContext *ctx = [[JSContext alloc] init];
        
        // 会在JS中生成Person对象,并且拥有所有值
        // 前提:Person对象必须遵守JSExport协议,
        ctx[@"person"] = p;
        
        // 执行JS代码
        // 注意:这里的person一定要跟上面声明的一样,因为生成的对象是用person引用
        // NSString *jsCode = @"person.play()";
        NSString *jsCode = @"person.playGame('德州扑克','晚上')";
        
        [ctx evaluateScript:jsCode];
    
    }
    
    

    4.1 JS调用OC系统类

    • 问题:系统自带的类,想要通过JS调用怎么办,我们又没办法修改系统自带类的文件

      • 和调用自定义类一样,也要弄个自定义协议继承JSExport,描述需要暴露哪些属性(想要把系统类的哪些属性暴露,就在自己的协议声明)
      • 通过runtime,给类添加协议
    • 自定义协议(UILabelJSExport)

    @protocol UILabelJSExport <JSExport>
    
    @property (nonatomic, strong) NSString *text;
    
    @end
    
    • JS调用OC系统类
    #pragma mark - JS调用OC系统类
    - (void)jsCallOCSystemClass
    {
        // 给系统类添加协议
        class_addProtocol([UILabel class], @protocol(UILabelJSExport));
        
        // 创建UILabel
        UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(50, 50, 100, 100)];
        [self.view addSubview:label];
        
        JSContext *ctx = [[JSContext alloc] init];
        // 就会在JS中生成label对象,并且用laebl引用
        ctx[@"label"] = label;
        
        // 利用JS给label设置文本内容
        NSString *jsCode = @"label.text = 'Oh Year'";
        
        [ctx evaluateScript:jsCode];
    }
    
    

    相关文章

      网友评论

      • 邵银岭:var factorial = function(n) {
        return n - 1
        };
        var A = {factorial:factorial};

        在oc代码里面调用A.factorial 方法好像不起作用,这个不支持读取js对象中的方法吗?
      • kyleBoy:受教了
      • WiseDarren:大神,请问一下,怎么样实现js调用oc方法,然后把方法结果返回给js啊,这一点没理顺。。。
      • 笨驴爱吃胡萝卜:不错,rn 的应用场景感觉大家还是在摸索
      • figure_ai:老师,有iOS转RN的学习路线么
      • 那位小姐:我可以直接添加jscore这个库,然后直接这样调用js,或者js调用oc吗?峥哥
        那位小姐:@袁峥Seemygo 恩 我刚刚试了 可以的 谢谢 涨知识了
        袁峥:@诸葛小亮 可以啊 这是苹果自带的原生库
      • 里奇001:JavaScriptCore可以调用foundation框架代码,苹果以后估计会禁用
        袁峥:你别去调啊
        里奇001:@袁峥Seemygo 可以调用iOS的私有api吗?
        袁峥:@里奇001 这个就是苹果提供的框架
      • SpringAnimation:留下足迹,帮助很大
      • 爱阿爸的阿龙龙:老师 我想问下 这个和那个第三方库WebViewJavaScripteBridge比 使用哪个好点呢?求给点参考意见 谢谢
        袁峥:@爱阿爸的阿龙龙868 这个看需求 webview底层就是用这个
      • gwk_iOS:大神rac系列的教程对我帮助很大,多谢。
        皮皮玥宝:没时间一个一个的瞅。。:grin:
        gwk_iOS:@皮皮玥宝 楼主的文章啊 里面很多干货
        皮皮玥宝:rac的教程在哪里?推荐下~
      • 彦子凡:666活捉一个熬夜的真老师

      本文标题:React Native源码中JavaScriptCore详解

      本文链接:https://www.haomeiwen.com/subject/ofisottx.html