首页 > 解决方案 > 无法将“A”类型的值转换为“B”

问题描述

所以我试图解决我在另一个问题中描述的问题(致命错误:使用未实现的初始化程序 'init(...)' for class '__lldb_expr_1.B'),但后来我遇到了另一个问题。

我创建了一个测试项目来隔离问题。我有一个A.swift包含以下内容的文件:

class A {
    private let i: Int

    private init(i: Int) {
        self.i = i
    }
}

extension A {
    convenience init() {
        self.init(i: 0)
    }
}

然后在另一个文件中我声明BA.

class B: A { }

为了测试这一点,我有一个简单ViewController的这样的:

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        let b: B = B()
        let a: A = b

        _ = a as! B
    }

}

这编译得很好,但是当我运行它时,它在向下转换aB. 这是我得到的错误:

Could not cast value of type 'MyTestProject.A' (0x1070bd570) to 
'MyTestProject.B' (0x1070bd4d8).

编辑:如果我在单独的文件中声明 A 和 B,我只会收到此错误。如果它们在同一个文件中声明,我会收到致命错误中描述的错误:使用未实现的初始化程序 'init(...)' for class '__lldb_expr_1.B'

标签: iosswift

解决方案


这当然是一个错误。确定错误是什么并不容易。

作为我的测试,我稍微修改了你的例子。在这个文件中,我们有:

class A {
    private let i: Int
    private init(i: Int) {
        self.i = i
    }
    convenience init() {
        self.init(i: 0)
    }
}

在我们的viewDidLoad

let a: A = B()
print(type(of:a))

这让事情变得简单而美好。好的,所以在另一个文件中,我们有

class B : A {}

那么,在我的测试中:

  • 在发布版本中,我们崩溃了:“使用未实现的初始化程序 'init(i:)' 用于类 'B'”(我相信你认识那个)

  • 在 Debug 构建中,我们不会崩溃,但会打印 A,这显然是错误的答案。

我可以添加复杂性(我尝试了很多东西),但我会添加以下观察:如果我们删除private之前的单词,所有问题都会消失init。通过这一更改,我们在 Debug 或 Release 中打印 B 并且不会崩溃。

所以我将推论这个问题与初始化器继承规则在底层实现的方式有关。它们似乎有能力在某些条件下打破多态性。(我的测试使用了 Xcode 11 beta 2,因此使用了 Swift 5.1。)


推荐阅读