記 Swift OC 混編隱式強(qiáng)制解包 Crash

swift 與 OC 混編引發(fā)了一個(gè)隱式強(qiáng)制解包 Crash冷尉,由于經(jīng)驗(yàn)不足走了一點(diǎn)彎路。

Crash 信息

Crash 信息大致如下:

 [inlined: Swift runtime failure: Unexpectedly found nil while implicitly unwrapping an Optional value

源代碼如下:

@implementation HTTPFileResponse
- (id)initWithFilePath:(NSString *)fpath forConnection:(HTTPConnection *)parent {...}
...

class MyHTTPFileResponse: HTTPFileResponse {
    let extraHeaders: [String: String]?
    init(filePath: String, for connection: HTTPConnection, extraHeaders: [String: String]?) {
        self.extraHeaders = extraHeaders
        // crash 在這里
        super.init(filePath: filePath, for: connection) 
    }
...

分析

只能看出是隱式強(qiáng)制解包引起的廉涕,直觀上看可能的變量就是 init 函數(shù)這三個(gè)入?yún)ⅲ珡倪壿嬌喜粦?yīng)該有解包操作艇拍,比較奇怪狐蜕,直接 debug 吧。

發(fā)現(xiàn) Crash 字符串讀取指令:

    0x1049d7dc8 <+48>:  add    x8, x8, #0x1d0            ; "Unexpectedly found nil while implicitly unwrapping an Optional value"
    0x1049d7dcc <+52>:  str    x8, [sp, #0x38]

那就找到讀取sp, #0x38的位置:

    0x1049d7ecc <+308>: ldr    x3, [sp, #0x38]
…
    0x1049d7f0c <+372>: bl     0x104ad4058               ; symbol stub for: Swift._assertionFailure(_: Swift.StaticString, _: Swift.StaticString, file: Swift.StaticString, line: Swift.UInt, flags: Swift.UInt32) -> Swift.Never

Swift._assertionFailure那這就是拋出 Crash 的函數(shù)了卸夕,往前面追溯层释,看什么分支會(huì)走到這個(gè)函數(shù):

    0x1049d7ea8 <+272>: ldur   x0, [x29, #-0x40]
    0x1049d7eac <+276>: subs   x8, x0, #0x0
    0x1049d7eb0 <+280>: cset   w8, eq
    0x1049d7eb4 <+284>: tbnz   w8, #0x0, 0x1049d7ec8     ; <+304> at MyHTTPFileResponse.swift
    0x1049d7eb8 <+288>: b      0x1049d7ebc               ; <+292> at MyHTTPFileResponse.swift
    0x1049d7ebc <+292>: ldur   x8, [x29, #-0x40]
    0x1049d7ec0 <+296>: str    x8, [sp, #0x28]
    0x1049d7ec4 <+300>: b      0x1049d7f14               ; <+380> at <compiler-generated>
    0x1049d7ec8 <+304>: ldr    x6, [sp, #0x40]
    0x1049d7ecc <+308>: ldr    x3, [sp, #0x38]

+284行判定若w8第 0 位不為 0 則跳轉(zhuǎn)到 Crash 鏈路,根據(jù)前面指令可知快集,需要[x29, #-0x40]讀取的值為空贡羔。

    0x1049d7e8c <+244>: bl     0x104ad335c               ; symbol stub for: objc_msgSendSuper2
    0x1049d7e90 <+248>: mov    x8, x0
    0x1049d7e94 <+252>: ldur   x0, [x29, #-0x50]
    0x1049d7e98 <+256>: stur   x8, [x29, #-0x40]

這個(gè)值來(lái)自于objc_msgSendSuper2函數(shù)返回值,我們知道這是調(diào)用父類函數(shù)个初,斷點(diǎn)在這個(gè)函數(shù)乖寒,把入?yún)?code>x0信息打出來(lái)(swift lldb 打印寄存器信息比較麻煩):

(lldb) re read x0
      x0 = 0x00000002832b1bc0
// 展開(kāi)內(nèi)存數(shù)據(jù)
(lldb) x 0x00000002832b1bc0
0x2832b1bc0: b5 72 a0 00 01 00 00 03 80 b6 9b 82 02 00 00 00  .r..............
0x2832b1bd0: 90 86 61 01 01 00 00 00 00 36 7f 80 02 00 00 00  ..a......6......
// 找到 isa 
(lldb) p/t 0x0300000100a072b5
(Int) 0b0000001100000000000000000000000100000000101000000111001010110101
// 查看 class 變量指針信息
(lldb) image lookup -a 0b100000000101000000111001010110000
      Address: AnyDemo[0x00000001001eb2b0] (AnyDemo.__DATA.__objc_data + 10248)
      Summary: (void *)0x0000000100a0e0d0: _Any0000MyHTTPFileResponse

再看一下調(diào)用代碼就瞬間明白了:

    init(filePath: String, for connection: HTTPConnection, extraHeaders: [String: String]?) {
        self.extraHeaders = extraHeaders
        // crash 在這里
        super.init(filePath: filePath, for: connection) 
    }

結(jié)論

在代碼層面,swift init函數(shù)省略了返回值勃黍,但這個(gè)代碼隱式表示返回值為非可選類型,所以在指令層面對(duì)super.init返回值做了檢查晕讲,如果返回為空則直接報(bào)錯(cuò)覆获,又由于super.init是 OC 代碼類型不安全,所以也能編譯通過(guò)瓢省。

觸發(fā) crash 原因就是 OC 這個(gè)構(gòu)造函數(shù)可能返回nil弄息,最終解法就是把 swift 構(gòu)造函數(shù)定義為可空構(gòu)造函數(shù)init?

也汲取了一個(gè)經(jīng)驗(yàn)勤婚,對(duì)于 swift 重寫 OC 帶返回值的函數(shù)摹量,最好無(wú)腦把 swift 返回值定義為可選類型,因?yàn)槟銦o(wú)法預(yù)估在將來(lái)的迭代過(guò)程中這個(gè)父類的 OC 函數(shù)會(huì)不會(huì)突然返回nil馒胆。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末缨称,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子祝迂,更是在濱河造成了極大的恐慌睦尽,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件型雳,死亡現(xiàn)場(chǎng)離奇詭異当凡,居然都是意外死亡山害,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門沿量,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)浪慌,“玉大人,你說(shuō)我怎么就攤上這事朴则∪ㄏ耍” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵佛掖,是天一觀的道長(zhǎng)妖碉。 經(jīng)常有香客問(wèn)我,道長(zhǎng)芥被,這世上最難降的妖魔是什么欧宜? 我笑而不...
    開(kāi)封第一講書人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮拴魄,結(jié)果婚禮上冗茸,老公的妹妹穿的比我還像新娘。我一直安慰自己匹中,他們只是感情好夏漱,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著顶捷,像睡著了一般挂绰。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上服赎,一...
    開(kāi)封第一講書人閱讀 51,708評(píng)論 1 305
  • 那天葵蒂,我揣著相機(jī)與錄音,去河邊找鬼重虑。 笑死践付,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的缺厉。 我是一名探鬼主播永高,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼提针!你這毒婦竟也來(lái)了命爬?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤辐脖,失蹤者是張志新(化名)和其女友劉穎遇骑,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體揖曾,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡落萎,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年亥啦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片练链。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡翔脱,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出媒鼓,到底是詐尸還是另有隱情届吁,我是刑警寧澤,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布绿鸣,位于F島的核電站疚沐,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏潮模。R本人自食惡果不足惜亮蛔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望擎厢。 院中可真熱鬧究流,春花似錦、人聲如沸动遭。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)厘惦。三九已至偷仿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間宵蕉,已是汗流浹背酝静。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留国裳,地道東北人形入。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓全跨,卻偏偏與公主長(zhǎng)得像缝左,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子浓若,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355

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