【iOS】SDK開發(fā)之自定義反射實(shí)現(xiàn)不定參數(shù)、不定數(shù)據(jù)類型方法的調(diào)用

前言

一般的開發(fā)者對(duì)“反射”這個(gè)詞很陌生,事實(shí)上他們自己用過了都不知道。但是在做聚合SDK開發(fā)當(dāng)中,用得最多的就是反射了。
現(xiàn)在我想實(shí)現(xiàn)反射調(diào)用該方法:

+ (NSString *)test:(id)test p1:(NSString *)p1 p2:(unsigned int)p2 p3:(double)p3 p4:(CGSize)p4 p5:(NSNull *)p5 p6:(void(^)(void))p6 p7:(BOOL)p7 p8:(char)p8 p9:(char *)p9 p10:(YY)p10 p11:(SEL)p11 p12:(void **)p12 p13:(void ***)p13 p14:(Class)p14 p15:(IMP)p15; // YY是自定義結(jié)構(gòu)體;p9是C字符串

然而遺憾的是,系統(tǒng)提供的反射最多只能帶2個(gè)參數(shù),而且參數(shù)只能是對(duì)象,但事實(shí)上并不是所有的參數(shù)都是對(duì)象的(比如 void **)。
雖然網(wǎng)上有很多的資料,但我都試過,都不咋地,總有缺陷。沒辦法,只能自己動(dòng)手?jǐn)]一個(gè)了。
實(shí)現(xiàn)多參數(shù)反射目前我只知道有2種方式:

  • runtime
  • NSInvocation

實(shí)現(xiàn)方式的選擇

雖然runtime號(hào)稱是萬能的,但事實(shí)上在64位里并不能直接使用objc_msgSend,必須強(qiáng)制轉(zhuǎn)換成對(duì)應(yīng)類型。可是,既然是不定參數(shù)、不定數(shù)據(jù)類型,所以我們根本無法去確定類型,換句話說,objc_msgSend是不可行的。
所以我們只能選擇NSInvocation了。

一些坑和需要解決的問題

眾所周知,OC是基于C的,事實(shí)上OC只是C的一層封裝,所以我們使用的東西也是C的東西。
而不定參數(shù)的實(shí)現(xiàn),也是用C的va_list。但va_list在取值之前是需要知道數(shù)據(jù)類型的,因?yàn)槊看稳≈刀家o個(gè)數(shù)據(jù)類型,然后va_list會(huì)根據(jù)該類型的長度去截取數(shù)據(jù),如果給的數(shù)據(jù)類型不對(duì),那截取的長度也會(huì)不對(duì),就會(huì)導(dǎo)致之后的數(shù)據(jù)都不對(duì)了。
當(dāng)然有人會(huì)說,那全部轉(zhuǎn)成一個(gè)類型不就好了?
我開始也是這么想的,事實(shí)上網(wǎng)上大多數(shù)人的做法也是這樣子。
但是,并不是所有類型都能轉(zhuǎn)的。比如全部用對(duì)象,那么void **和C字符串怎么轉(zhuǎn)呢?也有人會(huì)說C字符串包裝成NSString啊,嗯,這是沒問題的,可是你怎么還原呢?人家參數(shù)需要的是C字符串啊,你給個(gè)OC字符串??
另外,void *是一種類型,void **也是一種類型,char *也不一樣,怎么處理呢?
對(duì)于結(jié)構(gòu)體也是,每個(gè)自定義的結(jié)構(gòu)體的類型都是不一致的。
此外,NSInvocation是不支持自動(dòng)解包的。也就是說,如果你傳了NSNumber對(duì)象當(dāng)做參數(shù),而調(diào)用的方法需求參數(shù)是int,你不自己解包成int的話就會(huì)數(shù)據(jù)不對(duì)。
所以,當(dāng)下我們需要解決的問題就是數(shù)據(jù)類型的問題。

網(wǎng)上的一些實(shí)現(xiàn)方法

  • 網(wǎng)上很多方法都是通過把所有參數(shù)加入到一個(gè)數(shù)組里,然后遍歷數(shù)組把參數(shù)直接寫到NSInvocation,但我之前說了,并不是所有的類型都能轉(zhuǎn)成對(duì)象的,而且NSInvocation不支持自動(dòng)解包,這樣子當(dāng)遇到基本數(shù)據(jù)類型參數(shù)的時(shí)候,勢(shì)必會(huì)出錯(cuò)。
  • 有些人使用不定參數(shù)來,然后全部轉(zhuǎn)成對(duì)象,實(shí)際上和上面的大同小異。
  • 另外我還發(fā)現(xiàn)了挺多人為了調(diào)用方便,用分類來實(shí)現(xiàn)。但是做SDK最怕的就是重名了,分類的話,如果重名導(dǎo)致的問題還不好搞。因此,我封裝為類,如果有命名問題,只需要簡單的改個(gè)前綴即可。

我的實(shí)現(xiàn)思路

我觀察了NSString的聲明,得到了一些啟發(fā):

+ (instancetype)stringWithFormat:(NSString *)format, ...

在NSString的創(chuàng)建方法里,系統(tǒng)使用了format來提供信息,也就是說,當(dāng)匹配到%d的時(shí)候,就會(huì)認(rèn)為是int,這個(gè)時(shí)候在va_list里取值int不就好了?
不過我并不打算這樣子做,因?yàn)椴粏巫约焊愕寐闊{(diào)用者也麻煩,每次都要寫一串的%d%s什么的。
那么,有沒有別的方法可以預(yù)知所需要的參數(shù)類型呢?
當(dāng)然有了,NSMethodSignature里就有一個(gè)getArgumentTypeAtIndex:方法,該方法可以獲取到index位置參數(shù)的數(shù)據(jù)類型。到了這里,數(shù)據(jù)類型的問題我們就解決了一半了,也不需要想著怎么包裝成對(duì)象了,直接按照實(shí)際類型傳參豈不是更舒服?


接下來我們來解決void *、void **的問題。
對(duì)于指針類型,從一開始我就掉進(jìn)了一個(gè)坑。眾所周知,指針是一種派生類型,只要在某種類型后面加個(gè)*號(hào),就能派生出該類型的指針。換句話說,指針的類型的無數(shù)的,所以我們不可能是判斷無數(shù)的類型,但是,指針的儲(chǔ)存空間是固定的,也就是說我們?nèi)≈抵恍枰≈羔樀拈L度即可。


剩下的就是結(jié)構(gòu)體的問題了。
結(jié)構(gòu)體是基本數(shù)據(jù)類型的一個(gè)包裝,能實(shí)現(xiàn)無數(shù)的結(jié)構(gòu)體類型。所以除了內(nèi)置的常用結(jié)構(gòu)體,其它自定義的是無法識(shí)別的,因此,可以通過包裝成NAValue解決,而取值的時(shí)候,使用指針去取值就好了。

獲取返回值的問題

事實(shí)上返回值的類型也是各種各種的。但是我們并不需要判斷類型,我們只需要給一個(gè)指針就可以了,因?yàn)?code>- (void)getReturnValue:(void *)retLoc;這個(gè)方法只是簡單的把指針指向返回值的地址而已。
不過需要注意的是,在ARC環(huán)境下,如果不使用__weak或者__unsafe_unretained修飾對(duì)象的話就會(huì)崩潰,因?yàn)閺膇nvocation獲取的返回值并沒有為我們?cè)黾右糜?jì)數(shù),而我們定義變量時(shí),默認(rèn)就是__strong類型的,ARC就會(huì)假設(shè)該內(nèi)存塊已被retain(實(shí)際沒有),在出了定義域時(shí)就會(huì)release一次,導(dǎo)致返回值已經(jīng)是銷毀狀態(tài)了,從而導(dǎo)致崩潰。

當(dāng)使用__weak時(shí)控制臺(tái)會(huì)打印該錯(cuò)誤:Attempted to unregister unknown __weak variable at 0x7ffee9909f70. This is probably incorrect use of objc_storeWeak() and objc_loadWeak(). Break on objc_weak_error to debug.
該錯(cuò)誤可以無視,上面的地址0x7ffee9909f70其實(shí)就是我們給getReturnValue參數(shù)的地址,對(duì)此感興趣的同學(xué)可以用符號(hào)捕獲來調(diào)試(全局異常斷點(diǎn)是沒用的)。即在Symbolic BreakpointSymbolic里輸入objc_weak_error即可調(diào)試。


代碼就不貼了,這里只說實(shí)現(xiàn)的思路,想看具體的實(shí)現(xiàn)請(qǐng)到GitHub上下載源碼。
獲取源碼請(qǐng)點(diǎn)擊這里:ZTReflectionDemo
造個(gè)輪子不易,對(duì)你有用的請(qǐng)給個(gè)星星,謝謝了

iOS OC Swift Flutter開發(fā)群 139322447

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請(qǐng)通過簡信或評(píng)論聯(lián)系作者。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,546評(píng)論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,570評(píng)論 3 418
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,505評(píng)論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,017評(píng)論 1 313
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,786評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,219評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,287評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,438評(píng)論 0 288
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,971評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,796評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,995評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,540評(píng)論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,230評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,918評(píng)論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,697評(píng)論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,991評(píng)論 2 374

推薦閱讀更多精彩內(nèi)容

  • OC語言基礎(chǔ) 1.類與對(duì)象 類方法 OC的類方法只有2種:靜態(tài)方法和實(shí)例方法兩種 在OC中,只要方法聲明在@int...
    奇異果好補(bǔ)閱讀 4,306評(píng)論 0 11
  • 前天,富士康的同事給我打電話,相互之間調(diào)侃各自目前的狀況。通過他我了解到,成都富士康目前在做華為手機(jī)的訂單,這間接...
    反饋精靈閱讀 466評(píng)論 0 1
  • 世界就是一個(gè)無窮無盡的糾葛場,人,事,物,都處在其無限的糾纏中,看得到或看不到……
    幽蘭達(dá)人閱讀 189評(píng)論 0 0
  • 一、目的地:廣州MAG環(huán)球魔幻世界 交通:1.地鐵APM線“黃埔大道”站下,出站后乘坐扶手電梯上一層,轉(zhuǎn)左往b區(qū)方...
    kokojunua閱讀 722評(píng)論 0 0
  • 學(xué)習(xí)不順,甜品暖心。仙草南路的芋圓,想起來就自動(dòng)分泌唾液and不由自主的想做咀嚼運(yùn)動(dòng)(′?_?`)實(shí)在的芋頭的添加...
    火星民政局閱讀 311評(píng)論 0 0