JavaScriptCore学习之JSPatch源码阅读

JSPatch的基本原理:JS传递字符串给OC,OC通过Runtime接口调用和替换OC方法。

方法调用

1. require实现

  var _require = function(clsName) {
   
    if (!global[clsName]) {
      global[clsName] = {
      // _clsName表示这是一个OC的对象
        __clsName: clsName
      }
    } 
    return global[clsName]
  }

  global.require = function(clsNames) {
   
    var lastRequire
    clsNames.split(',').forEach(function(clsName) {
   
      lastRequire = _require(clsName.trim())
    })
    return lastRequire
  }

通过这个方法可以看到requre(‘UIView’)这个函数只做了什么,在全局作用域上声明一个对应的变量 UIView,它是类型是一个对象,这个对象有个属性 __clsName 就是’UIView’。

通过clsNames.split(‘,’).foreach这行代码,我们可以看到require一次包含多个对象是如何实现的。

2. JS接口

由于JS不存在OC/ruby那样的消息转发机制,JSPatch作者做了一个很有意思的处理,对JS脚本做个简单编译。将所有JS的方法调用都通过正则进行替换,统一调用 __c() 函数,然后再进行分发。例如下面的转换:

UIView.alloc().init() –> UIView.__c('alloc')().__c('init')()

下面我们可以看一下 __c()需要返回一个function,然后这个function会被调用,下面是__c元函数的源代码

// 为Object的原型定义一个称之为__c的属性,它的值是返回一个function返回值的function,并且设置这个属性configurable:false, enumerable: false。
// JS通过这种为prototype原型添加属性的方式,为所有的JS对象都添加了这个方法。JS对象调用__c方法的时候,通过调用链就可以找到这个方法。
Object.defineProperty(Object.prototype, "__c", {value: function(methodName) {
   
    // ??
    if (this instanceof Boolean) {
      return function() {
   
        return false
      }
    }

    // 如果对象的__obj和__clsName都是空,表明这是一个JS对象,在对象中查找并返回对应的方法即可
    if (!this.__obj && !this.__clsName) {
      // 如果在这个JS对象中根本没有这个方法,抛出异常
      if (!this[methodName]) {
        throw new Error(this + '.' + methodName + ' is undefined')
      }
      // 调用bind(this)跟JS的语法特性有关系,绑定返回的这个方法的上下文执行环境为这个对象自身,否则调用的时候,方法内部的this会指向其他离它最近的一个执行环境
      return this[methodName].bind(this);
    }

    // 这里使用self变量保存this的原因是,如果我们直接返回这个function,在里面使用this,指向的就不是当前调用这个函数的对象,所以需要使用self保持this,当然也可以使用bind
    var self = this
    // 在调用super的时候,做了一个特殊处理
    if (methodName == 'super') {
      return function() {
   
        if (self.__obj) {
          self.__obj.__clsDeclaration = self.__clsDeclaration;
        }
        // 返回一个新的对象,这个对象的__isSuper标识等于1,在OC调用执行的时候,会判断这个标识来决定执行哪一个方法
        return {__obj: self.__obj, __clsName: self.__clsName, __isSuper: 1}
      }
    }

    /**
     *  _methodFunc的作用是把JS的相关调用信息传递给OC,然后OC通过runtime来执行这些调用,返回结果
     */

    if (methodName.indexOf('performSelector') > -1) {
      if (methodName == 'performSelector') {
       // return的这个function的作用是:直接调用OC对应的SEL,而不是通过OC调用performSelector,与最下面那个直接调用OC方法的function不同的是关于参数的处理
        return function(){
   
          var args = Array.prototype.slice.call(arguments)
          return _methodFunc(self.__obj, self.__clsName, args[0], args.splice(1), self.__isSuper, true)
        }
      } else if (methodName == 'performSelectorInOC') {
        // 这个与上面的区别在于,这里的调用是异步,为了防止出现线程竞争问题,调用完毕之后进行callback回调
        // refer: https://github.com/bang590/JSPatch/wiki/performSelectorInOC-%E4%BD%BF%E7%94%A8%E6%96%87%E6%A1%A3
        return function(){
   
          var args = Array.prototype.slice.call(arguments)
          return {__isPerformInOC:1, obj:self.__obj, clsName:self.__clsName, sel: args[0], args: args[1], cb: args[2]}
        }
      }
    }

    // return的这个function的作用是:处理参数,通过_methodFunc进行OC调用,然后返回执行结果
    return function(){
   
      var args = Array.prototype.slice.call(arguments)
      return _methodFunc(self.__obj, self.__clsName, methodName, args, self.__isSuper)
    }
  }, configurable:false, enumerable: false})

3. 消息传递

JS调用OC函数时,JavaScriptCore会自动会参数和返回值进行转换。基本类型会自动进行转换,详细的转换参考JavaScriptCore的API

通过上面的讲解我们知道,JSPatch主要是通过_methodFunc方法调用OC的消息

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值