在项目中谨慎为系统类添加分类!!!!!
結論:
1、堅決杜絕為系統類做方法交換(見到【class_replaceMethod】格殺勿論!)
2、為系統類添加分類時候,屬性和方法名必須加上【世上獨一無二】的前綴,避免沖突和混淆。
?
之所以讓我對上述行為恨之入骨是因為,今天為了一個bug,我花了將近半天時間苦苦追尋原因。
我只是使用了一個簡簡單單的UIImagePickerController的拍照的功能界面,奇葩的現象是,點擊快門按鈕時---可以看見界面中的按鈕發生了視覺上的響應,但是卻沒有功能上的響應(按道理,我這邊按下按鈕的時候,拍照就會完成輸出圖片數據)。
我的整個思考過程是這樣的:
點擊沒功能反應?難道有誰把這個類中的響應方法重寫了?
---尋找UIImagePickerController在整個項目中的出現,看有沒有對它做分類,或者是子類化。結果是沒有的!
那是不是關于UIImagePickerController這個類,隨著iOS的SDK的更新,我有些屬性或者方法需要適配下?
---我用iOS10.2和iOS11.2和最新的iOS11.4都看了一遍,都有這個問題。難道從iOS10開始就要有些跟之前不一樣的適配需要做?我翻看相關的適配博客,沒有發現!
難道是我對事件響應鏈做了一些調整?導致事件被阻斷?
---我回頭看了一眼UIImagePickerController對象創建后使用的是模態出來的,一個簡單的展示鏈,沒有問題!
難道多線程問題?
---NONONO!我核對了下代碼,整個過程都在主線程中,至于就算UIImagePickerController里面的處理上開了子線程,那也不歸我們管,它暴露出來的API肯定是在主線程的。
那就見鬼了~但是,不對啊,就這個簡單的UIImagePickerController,不至于啊!
---我應該是知道肯定是項目中的其他SDK的環境影響到了它,但是會是什么呢?為了更加確定我的這個想法,給自己繼續追尋原因的信心,我新建demo,這塊代碼原樣放入。臥槽,完美運行。
那行,我這樣的話,我要一查到底!
---能夠引起這個問題的全局原因,那么就是項目的配置數據有誤,那么就是分類的原因。
項目的配置數據就是那些,最多就是在info文件中說明下使用相機的原因,方便獲取用戶的授權。因此我肯定,這塊沒有問題。
分類的話,我已經確定了沒有UIImagePickerController的分類。那么肯定就是其他系統類的分類了。
首先,添加分類的不可控性體現在:
(1)如果在分類中重寫類的方法,分類的重寫優先級是最高的。
(2)如果系統對UIImagePickerController添加了一些分類(包括不暴露在API中的),剛好又與項目中對其的分類方法名重復,會后入為主的。
(3)另外分類是會在編譯器就全部加上的,如果在分類中對類本身做的處理是會影響到類本身的。也就是說,如果對類中的方法做了方法轉移的處理,那就無形中影響了。
于是我趕緊搜索方法轉移的class_replaceMethod方法名有沒有在項目中出現。果然,項目中對UIButton的分類中重寫了+load類方法,在改方法中做了方法轉移!
正如前面分析的,重寫+load方法的優先級:分類中>子類中>類本身。
并且重寫的是+load這個方法,完全可以做到悄無聲息。
為了進一步驗證就是這個原因,我直接將這個分類的實現方法注釋掉,然后運行項目~【method_exchangeImplementations完美運作!!】
剛剛時候的是相當于反編譯的方式把問題的根源找到了,現在我需要的是使用順推的方法,把問題的出現原因梳理清楚。
通過查看UIButton的這個分類知道,它是將@selector(sendAction:to:forevent:)這個方法替換掉了。sendAction:to:forevent:方法中實際調用的是objc_setAssociatedObject,替換后的方法,在其中加了一個計時器,使得規定時間內,只能objc_setAssociatedObject調用一次。
這樣的做法,應該是為了防止button高頻按動而做的改動。
然而,UIImagePickerController功能界面中的快門按鈕,實際上是在拍照功能時,按住快門鍵不放,可以實現高頻連拍的功能(我試了下最多時999張),這樣的話,就很好解釋通了。雖然,按住快門鍵按鈕不放是一個“長按”手勢,但是其內部的實現肯定是高頻的調用@selector(sendAction:to:forevent:)這個方法。說到這里,我得說明下,雖然長按手勢和單點手勢表面上的確是不一樣的,但是其內部都調用了@selector(sendAction:to:forevent:)這個方法。因此,之前寫button這個分類的目的雖然是防止用戶高頻的單擊按鈕,但是現在用戶雖然不是高頻的單擊,而是長按,但是都調用的是@selector(sendAction:to:forevent:)這個方法。畢竟,當初為了防止用戶高頻單擊,是替換掉了@selector(sendAction:to:forevent:)這個方法。因此,謎底揭開了,整個離奇的故事真相大白~
轉載于:https://www.cnblogs.com/cchHers/p/9397085.html
總結
以上是生活随笔為你收集整理的在项目中谨慎为系统类添加分类!!!!!的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: REST、RESTful 与 RESTf
- 下一篇: 小程序图片显示问题