Swift 調(diào)用 Objective-C 的可變參數(shù)函數(shù)

這個問題是一個朋友問我怎么寫腺劣,一開始我是拒絕的。我想這種東西網(wǎng)上隨便 google 下不就有了嗎因块。他說橘原,查了,但沒大看明白贮聂。于是我就查了下靠柑,沒想到這個寫法確實有點詭異寨辩,我第一反應(yīng)也沒看明白吓懈。所以隨便水一篇文章,強行完成本周的博客任務(wù)靡狞,順便給朋友一個交代耻警。

本文分為兩部分,第一部分是 Swift 怎么調(diào)用 Objective-C 的可變參數(shù)函數(shù),第二部分是 Objective-C 怎么調(diào)用 Swift 的可變參數(shù)函數(shù)甘穿。

Swift 調(diào)用 Objective-C 的可變參數(shù)函數(shù)

先寫一個例子

隨便寫一個 Objective-C 的可變參數(shù)函數(shù):接受 n 個 String 類型的參數(shù)腮恩,把它們一個一個地打印出來,然后返回參數(shù)一共有多少個温兼。這個方法毫無意義秸滴,只是為了強行有個返回值做例子編出來的而已……

- (NSInteger)foo:(NSString *)value,...
{
  va_list list;
  va_start(list, value);
  NSInteger count = 0;
  while (YES)
  {
    NSString *string = va_arg(list, NSString*);
    if (!string) {
      break;
    }
    NSLog(@"%@",string);
    count++;
  }
  va_end(list);
  return count;
}

這個方法直接在 swift 里調(diào)是調(diào)不了的。為了想要在 swift 里調(diào)用募判,需要把它稍微改造下荡含。

怎么改造一下

  1. 把方法簽名里的 ,... 改成一個參數(shù) args:(va_list)list
  2. va_list list;va_start(list, value); 這兩句需要去掉,因為我們的 va_list 是傳進來的届垫。va_end 應(yīng)該也可以去掉了释液,不去掉也不會報錯,也許也可以保留著作為一個 good practice 吧装处。

改完之后的 Objective-C 方法:

- (NSInteger)foo:(va_list)list
{
  NSInteger count = 0;
  while (YES)
  {
    NSString *string = va_arg(list, NSString*);
    if (!string) {
      break;
    }
    NSLog(@"%@",string);
    count++;
  }
  return count;
}

在 Swift 里怎么調(diào)用

既然 va_list 是作為一個參數(shù)傳進去的误债,關(guān)鍵是要用特殊方法構(gòu)造一個 va_list。就跟在 Objective-C 里可以用 malloc 來強行構(gòu)造 va_list 一樣妄迁,Swift 里也有辦法寝蹈,有一個函數(shù)可以用:

public func withVaList<R>(_ args: [CVarArg], _ body: (CVaListPointer) -> R) -> R

這個函數(shù)的形式看起來不大常見,其實也很簡單登淘,它就是接受一個數(shù)組作為第一個參數(shù)躺盛,第二個參數(shù)是個閉包,閉包的參數(shù)就是生成好的va_list形帮,而返回值你隨便返回什么都可以槽惫,閉包的返回值就是整個函數(shù)的返回值。

換句話說辩撑,就是你先傳給它一個數(shù)組界斜,讓它根據(jù)這個數(shù)組構(gòu)造 va_list;然后它把構(gòu)造好的 va_list 用閉包的參數(shù)傳回來給你合冀,那么在閉包里這個 va_list 就隨你怎么用了各薇;如果閉包里你有什么結(jié)果想傳出去的,可以作為閉包的返回值返回君躺,它就會作為這個函數(shù)的返回值傳出去峭判,接受了這個返回值,后面就隨你怎么用了棕叫。

let testClass = TestClass()
let count = withVaList(["hello", "hamster", "good", "morning"]) { args -> Int in
   return testClass.foo(args)
}
print(count)

輸出:

hello
hamster
good
morning
4

文檔里說了林螃,這個生成的 va_list 只許你在閉包里用,你不許把它傳出去在外面用俺泣,不然不保證 valid疗认。讓我們皮一下試試……

let testClass = TestClass()
let args = withVaList(["hello", "hamster", "good", "morning"]) { args -> CVaListPointer in
  return args
}
print(testClass.foo(args))

結(jié)果是 crash完残,EXC_BAD_ACCESS,估計是到了閉包外面那塊空間已經(jīng)被釋放掉了横漏。這也從側(cè)面證明了不需要再寫 va_end 了吧……

還有另一個類似的函數(shù) getVaList谨设,把 va_list 作為返回值返回出來的,寫法更簡潔缎浇,把上面的寫法改改就是這樣:

let count = testClass.foo(getVaList(["hello", "hamster", "good", "morning"]))
print(count)

但是文檔明確說了兩點:

  1. 能用 withVaList 就不要用 getVaList扎拣。具體原因沒說。
  2. 那為啥還要提供給你這個方法呢素跺?是因為有些情況語言規(guī)則不讓用 withVaList鹏秋,比如在 class initializer 里。這時候就只好用 getVaList 了亡笑。

包裝成 Swift 的可變參數(shù)方法

上面這語法侣夷,如果要用得很多,每次都這么寫怪煩的仑乌。我們可以給它包裝成一個 Swift 的可變參數(shù)方法……

extension TestClass {
  func foo(_ strings: String...) -> Int {
    return withVaList(strings) { args -> Int in
      return foo(args)
    }
  }
}

然后調(diào)用的時候就一勞永逸了:

let testClass = TestClass()
let count = testClass.foo("hello", "hamster", "good", "morning")
print(count)

感慨下 Swift 的語法簡潔太多了百拓,不是嗎?

Objective-C 調(diào)用 Swift 的可變參數(shù)函數(shù)

既然 Swift 的語法這么簡潔晰甚,我們干脆把可變參數(shù)方法都在 Swift 里實現(xiàn)衙传,然后讓 Objective-C 來調(diào)唄?

然而 Swift 無情地拒絕了:

Screen Shot 2018-03-24 at 4.49.38 PM.png

真的要調(diào)怎么辦厕九?只好另寫一個接受數(shù)組為參數(shù)的方法蓖捶,在 Objective-C 里調(diào)這個方法,或者再寫一個 Objective-C 的可變參數(shù)方法把它 wrap 一層了……

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末扁远,一起剝皮案震驚了整個濱河市俊鱼,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌畅买,老刑警劉巖并闲,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異谷羞,居然都是意外死亡帝火,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進店門湃缎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來犀填,“玉大人,你說我怎么就攤上這事嗓违【叛玻” “怎么了?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵靠瞎,是天一觀的道長比庄。 經(jīng)常有香客問我求妹,道長乏盐,這世上最難降的妖魔是什么佳窑? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮父能,結(jié)果婚禮上神凑,老公的妹妹穿的比我還像新娘。我一直安慰自己何吝,他們只是感情好溉委,可當我...
    茶點故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著爱榕,像睡著了一般瓣喊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上黔酥,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天藻三,我揣著相機與錄音,去河邊找鬼跪者。 笑死棵帽,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的渣玲。 我是一名探鬼主播逗概,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼忘衍!你這毒婦竟也來了逾苫?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤枚钓,失蹤者是張志新(化名)和其女友劉穎隶垮,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體秘噪,經(jīng)...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡狸吞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了指煎。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蹋偏。...
    茶點故事閱讀 40,865評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖至壤,靈堂內(nèi)的尸體忽然破棺而出威始,到底是詐尸還是另有隱情,我是刑警寧澤像街,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布黎棠,位于F島的核電站晋渺,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏脓斩。R本人自食惡果不足惜木西,卻給世界環(huán)境...
    茶點故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望随静。 院中可真熱鬧八千,春花似錦、人聲如沸燎猛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽重绷。三九已至沸停,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間昭卓,已是汗流浹背愤钾。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留葬凳,地道東北人绰垂。 一個月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像火焰,于是被迫代替她去往敵國和親劲装。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,870評論 2 361

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