JSPatch 常見問題(一)

納蘭若水發表於2017-12-22

接觸JSPatch也快一年,在此總結歸納一些新接觸的開發者容易碰到的一些問題。其實很多問題bang哥在wiki中已經寫的很詳細了,建議花點時間仔細看看下面這兩個WiKi內容。

想要了解JSPatch實現原理可以先看 JSPatch 實現原理詳解

require匯入類

只需要一對' ', 多個類之間用逗號 ,分隔:

require('UIView, UIColor')
複製程式碼
方法遇到下劃線_需要特別注意

因為JSPatch是通過_來分割方法引數的,所以遇到方法本身帶有_的時候需要使用__雙下劃線代替,否則會被識別成一個引數,導致出現類似如下的報錯。

報錯資訊

//OC方法程式碼如下
- (BOOL)_test_Bool {
    NSLog(@"我是oc testBool");
    return NO;
}
//JS錯誤呼叫程式碼如下
var  testBool = self._test_Bool();
複製程式碼

我們知道selector 的 一個:代表有一個引數,結合報錯資訊可知runtime所找的方法是帶兩個引數的,所以肯定跟你需要呼叫的方法不一致,找不到則會導致崩潰。 把JS呼叫程式碼改為下面這種即可,每個下劃線_變成雙_,同理如果你方法中本身是雙下劃線__則需要變成____四個下劃線。

//JS正確呼叫程式碼如下
var  testBool = self.__test__Bool();
複製程式碼

Safari控制檯列印結果

如果需要替換的方法本身帶有下劃線,與方法呼叫相同,也同樣需要雙下劃線__

__test__Bool:function(){
   console.log("我是js  testBool");
   return YES;
}
複製程式碼

替換成功

undefined is not an object(evaluating 'self.__c')

提示意思是self這個物件不存在,結合JSPatch的使用場景可以推測出這個錯誤一般是由於在block中使用self引起的,解決辦法參考block 裡使用 self 變數。但值得注意的是,在JS中,如果報了類似的undefined is not an object aaa.bbb錯誤,則是因為aaa這個物件為undefined,而你又要訪問一個undefined的物件屬性或者方法在JS中會導致崩潰。 ####一個最簡單高效獲取物件類名方法 大多數需求場景下可以通過object.__clsName 的方式獲取OC物件類名。不需要通過isKindOfClass或者NSStringFromClass(此方法需要預先新增C函式擴充套件)來獲取,而且不需要與OC相互通訊。

//替換按鈕點選方法
handleBtn: function(sender) {
     var btnClassName = sender.__clsName;
     console.log("btnClassName is : " + btnClassName);
}
複製程式碼

圖一:Safati斷點檢視獲取的Button物件

圖二:控制檯列印物件類名

下面列舉一些有__clsName屬性的OC物件情況。

  • JSPatch 通過hook訊息轉發forwardInvocation: 方法,在呼叫替換方法或者新新增的方法時給引數新增了__clsName標記來儲存類名(所以類似圖一的sender引數就會有__clsName屬性),但是Class、NSNumber、BOOL、CGFloat、結構體不知道還有沒有o(╯□╰)o,感興趣的可以去檢視原始碼除外。
  • 通過 require 引入的類,所以在JS內通過init出來的物件都會有__clsName屬性。
  • 屬性和私有變數值和方法返回值(Class、NSNumber、BOOL、CGFloat、結構體不知道還有沒有o(╯□╰)o感興趣的可以去檢視原始碼除外,不過這些好像需要知道類名情況比較少)
  • 歡迎補充

####OC物件和JS物件 大家在JSPatch交流群裡所說的OC物件指的是從OC中獲取的JS物件如var str = self.str() 或 通過JS初始化的物件var str = NSString.stringWithFormat("Hello World"),而JS物件指的是例如 var str = "我是js物件"var str = self.str().toJS()之後的JS物件。OC物件應該使用OC中相應方法,JS物件應該使用JS中相應方法。

//替換按鈕點選方法
handleBtn: function(sender) {
     var btnClassName = sender.__clsName;
     console.log("btnClassName is : " + btnClassName);
      //初始化的OC物件
     var ocInitStr = NSString.stringWithFormat("Hello World")
      //獲取屬性得到的OC物件
     var str = self.str();
     console.log("ocInitStr 擷取前5個字元:" + ocInitStr.substringToIndex(5).toJS())
      //JS物件
     var jsStr = "我是JS物件";
     console.log("jsStr 擷取前4個字元:" + jsStr.substr(0,4))
}
複製程式碼

列印結果
####JS中如何使用強轉 答:不需要強轉,直接當成強轉之後的型別使用,JS裡面沒有型別的概念,都是物件。 ####列舉 直接使用值 Note:可在Xcode用NSLog列印檢視具體值

####Block還未被賦值就被呼叫的問題 js exception, msg: block is not a function. (In 'block(xxx,...., xxx)', 'block' is false) 類似的報錯通常是因為你獲取的block還沒有被賦值,所以在js中沒有正確的執行,解決辦法是獲取到block之後,判斷是否存在:

//示例方法
xxxfunction: function(sender) {
     var block = self.testBlock();
     if(block){//加上這個判斷
          //do something with block
          block(xxx,...,xxx);
      }
}
複製程式碼