問題描述:
調(diào)用系統(tǒng)相機(jī)蔚晨,點(diǎn)擊拍照, 發(fā)現(xiàn)只能長按才可以,點(diǎn)擊無響應(yīng)。
排查的結(jié)果是:
重寫了一個button 的類別 ,導(dǎo)致拍照時的Button事件出現(xiàn)問題
結(jié)論:
在項(xiàng)目中謹(jǐn)慎為系統(tǒng)類添加分類
如下作者做了很詳細(xì)的說明:
1肛循、堅決杜絕為系統(tǒng)類做方法交換(見到【class_replaceMethod】格殺勿論!)
2银择、為系統(tǒng)類添加分類時候多糠,屬性和方法名必須加上【世上獨(dú)一無二】的前綴,避免沖突和混淆浩考。
之所以讓我對上述行為恨之入骨是因?yàn)榧锌祝裉鞛榱艘粋€bug,我花了將近半天時間苦苦追尋原因析孽。
我只是使用了一個簡簡單單的UIImagePickerController的拍照的功能界面搭伤,奇葩的現(xiàn)象是,點(diǎn)擊快門按鈕時---可以看見界面中的按鈕發(fā)生了視覺上的響應(yīng)袜瞬,但是卻沒有功能上的響應(yīng)(按道理怜俐,我這邊按下按鈕的時候,拍照就會完成輸出圖片數(shù)據(jù))邓尤。
我的整個思考過程是這樣的:
點(diǎn)擊沒功能反應(yīng)拍鲤?難道有誰把這個類中的響應(yīng)方法重寫了?
---尋找UIImagePickerController在整個項(xiàng)目中的出現(xiàn)汞扎,看有沒有對它做分類季稳,或者是子類化。結(jié)果是沒有的澈魄!
那是不是關(guān)于UIImagePickerController這個類景鼠,隨著iOS的SDK的更新,我有些屬性或者方法需要適配下痹扇?
---我用iOS10.2和iOS11.2和最新的iOS11.4都看了一遍铛漓,都有這個問題。難道從iOS10開始就要有些跟之前不一樣的適配需要做鲫构?我翻看相關(guān)的適配博客票渠,沒有發(fā)現(xiàn)!
難道是我對事件響應(yīng)鏈做了一些調(diào)整芬迄?導(dǎo)致事件被阻斷问顷?
---我回頭看了一眼UIImagePickerController對象創(chuàng)建后使用的是模態(tài)出來的,一個簡單的展示鏈,沒有問題杜窄!
難道多線程問題肠骆?
---NONONO!我核對了下代碼塞耕,整個過程都在主線程中蚀腿,至于就算UIImagePickerController里面的處理上開了子線程,那也不歸我們管扫外,它暴露出來的API肯定是在主線程的莉钙。
那就見鬼了~但是,不對啊筛谚,就這個簡單的UIImagePickerController磁玉,不至于啊驾讲!
---我應(yīng)該是知道肯定是項(xiàng)目中的其他SDK的環(huán)境影響到了它蚊伞,但是會是什么呢?為了更加確定我的這個想法吮铭,給自己繼續(xù)追尋原因的信心时迫,我新建demo,這塊代碼原樣放入谓晌。臥槽掠拳,完美運(yùn)行。
那行纸肉,我這樣的話碳想,我要一查到底!
---能夠引起這個問題的全局原因毁靶,那么就是項(xiàng)目的配置數(shù)據(jù)有誤胧奔,那么就是分類的原因。
項(xiàng)目的配置數(shù)據(jù)就是那些预吆,最多就是在info文件中說明下使用相機(jī)的原因龙填,方便獲取用戶的授權(quán)。因此我肯定拐叉,這塊沒有問題岩遗。
分類的話,我已經(jīng)確定了沒有UIImagePickerController的分類凤瘦。那么肯定就是其他系統(tǒng)類的分類了宿礁。
首先,添加分類的不可控性體現(xiàn)在:
(1)如果在分類中重寫類的方法蔬芥,分類的重寫優(yōu)先級是最高的梆靖。
(2)如果系統(tǒng)對UIImagePickerController添加了一些分類(包括不暴露在API中的)控汉,剛好又與項(xiàng)目中對其的分類方法名重復(fù),會后入為主的返吻。
(3)另外分類是會在編譯器就全部加上的姑子,如果在分類中對類本身做的處理是會影響到類本身的。也就是說测僵,如果對類中的方法做了方法轉(zhuǎn)移的處理街佑,那就無形中影響了。
于是我趕緊搜索方法轉(zhuǎn)移的class_replaceMethod方法名有沒有在項(xiàng)目中出現(xiàn)捍靠。果然沐旨,項(xiàng)目中對UIButton的分類中重寫了+load類方法,在改方法中做了方法轉(zhuǎn)移榨婆!
正如前面分析的磁携,重寫+load方法的優(yōu)先級:分類中>子類中>類本身。
并且重寫的是+load這個方法纲辽,完全可以做到悄無聲息。
為了進(jìn)一步驗(yàn)證就是這個原因璃搜,我直接將這個分類的實(shí)現(xiàn)方法注釋掉拖吼,然后運(yùn)行項(xiàng)目~【method_exchangeImplementations完美運(yùn)作!这吻!】
剛剛時候的是相當(dāng)于反編譯的方式把問題的根源找到了吊档,現(xiàn)在我需要的是使用順推的方法,把問題的出現(xiàn)原因梳理清楚唾糯。
通過查看UIButton的這個分類知道怠硼,它是將@selector(sendAction:to:forevent:)這個方法替換掉了。sendAction:to:forevent:方法中實(shí)際調(diào)用的是objc_setAssociatedObject移怯,替換后的方法香璃,在其中加了一個計時器,使得規(guī)定時間內(nèi)舟误,只能objc_setAssociatedObject調(diào)用一次葡秒。
這樣的做法,應(yīng)該是為了防止button高頻按動而做的改動嵌溢。
然而眯牧,UIImagePickerController功能界面中的快門按鈕抑钟,實(shí)際上是在拍照功能時革娄,按住快門鍵不放护奈,可以實(shí)現(xiàn)高頻連拍的功能(我試了下最多時999張)蚁飒,這樣的話徽曲,就很好解釋通了渣淤。雖然户誓,按住快門鍵按鈕不放是一個“長按”手勢尚洽,但是其內(nèi)部的實(shí)現(xiàn)肯定是高頻的調(diào)用@selector(sendAction:to:forevent:)這個方法。說到這里阀坏,我得說明下如暖,雖然長按手勢和單點(diǎn)手勢表面上的確是不一樣的,但是其內(nèi)部都調(diào)用了@selector(sendAction:to:forevent:)這個方法忌堂。因此盒至,之前寫button這個分類的目的雖然是防止用戶高頻的單擊按鈕,但是現(xiàn)在用戶雖然不是高頻的單擊士修,而是長按枷遂,但是都調(diào)用的是@selector(sendAction:to:forevent:)這個方法。畢竟棋嘲,當(dāng)初為了防止用戶高頻單擊酒唉,是替換掉了@selector(sendAction:to:forevent:)這個方法。因此沸移,謎底揭開了痪伦,整個離奇的故事真相大白~
,也可以附上我的文章出處:http://www.cnblogs.com/cchHers/