美文网首页
记 Swift OC 混编隐式强制解包 Crash

记 Swift OC 混编隐式强制解包 Crash

作者: 波儿菜 | 来源:发表于2024-10-27 17:32 被阅读0次

swift 与 OC 混编引发了一个隐式强制解包 Crash,由于经验不足走了一点弯路。

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) 
    }
...

分析

只能看出是隐式强制解包引起的,直观上看可能的变量就是 init 函数这三个入参,但从逻辑上不应该有解包操作,比较奇怪,直接 debug 吧。

发现 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 的函数了,往前面追溯,看什么分支会走到这个函数:

    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 则跳转到 Crash 链路,根据前面指令可知,需要[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]

这个值来自于objc_msgSendSuper2函数返回值,我们知道这是调用父类函数,断点在这个函数,把入参x0信息打出来(swift lldb 打印寄存器信息比较麻烦):

(lldb) re read x0
      x0 = 0x00000002832b1bc0
// 展开内存数据
(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

再看一下调用代码就瞬间明白了:

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

结论

在代码层面,swift init函数省略了返回值,但这个代码隐式表示返回值为非可选类型,所以在指令层面对super.init返回值做了检查,如果返回为空则直接报错,又由于super.init是 OC 代码类型不安全,所以也能编译通过。

触发 crash 原因就是 OC 这个构造函数可能返回nil,最终解法就是把 swift 构造函数定义为可空构造函数init?

也汲取了一个经验,对于 swift 重写 OC 带返回值的函数,最好无脑把 swift 返回值定义为可选类型,因为你无法预估在将来的迭代过程中这个父类的 OC 函数会不会突然返回nil

相关文章

  • oc Swift 混编

    oc Swift 混编 oc 项目 混编Swift1.1 oc 调用 Swift 的类 和 方法步骤: ...

  • Swift

    混编 15、OC与Swift的混编_海森堡_lichangan的博客-CSDN博客_oc swift 混编[htt...

  • OC和Swift混编手动创建桥接文件及命名空间

    Obj-C混编Swift && Swift混编Obj-C Swift引用OC实现通过桥接头文件,OC引用Swift...

  • OC和Swift混编一

    OC工程下混编 Swift工程下混编请查看OC和Swift混编二 1.建一个OC工程命名为OCTestSwift ...

  • OC和Swift混编二

    Swift工程下混编 OC工程下混编请查看OC和Swift混编一 1.建一个Swift工程命名为SwiftTest...

  • Swift(总)

    Swift目录如下: Objective-C和Swift混编指南-s混编-OC&Swift[https://www...

  • OC与Swift混编 / Mix & Match

    OC与Swift混编 / Mix & Match OC in Swift 通过Header File创建Bridg...

  • Swift:你一定不知道的枚举

    升级xCode10,项目中发现了一些crash.挺有意思,记录一下.项目为OC和Swift混编. 案例 swift...

  • OC-Swift混编

    OC项目中添加Swift文件以及Swift项目中添加OC文件 OC类引用Swift类,需倒入隐式头文件 "xxx-...

  • OC Swift文件混编

    一.Swift 类可以继承 OC 类,OC 类不能继承 Swift 类。 二.Swift 和 OC 混编 三.两个...

网友评论

      本文标题:记 Swift OC 混编隐式强制解包 Crash

      本文链接:https://www.haomeiwen.com/subject/lvbjdjtx.html