Swift通用虚方法调用导致段错误

时间:2014-12-12 03:38:37

标签: generics inheritance swift virtual-method

我是Swift的新手,所以我可能会做一些愚蠢的事情。如果是这样,很好:请告诉我在哪里!

在以下代码中,您会看到类Derived继承自通用类Base<T>

class Base<T> {
  func method(Int) -> T {
    fatalError("Subclasses must override method.")
  }
}

class Derived<T> : Base<Int> {
  override func method(input:Int) -> Int {
    return input
  }
}

在Swift 1.1中,非泛型类不可能从泛型类继承。因此,在这种情况下,Derived具有虚拟类型变量。

如果我现在使用这些类:

class Container {
  let item: Base<Int>
  init(item:Base<Int>) {
    self.item = item
  }
  func method(input:Int) -> Int {
    return item.method(input)
  }    
}
let a = Derived<Int>()
let b = Container(item:a)
let test = b.method(42)

代码编译得很好,但是在调用Derived<Int>.method时会出现段错误。根据我到目前为止的调试情况,看起来self指针在Container.method内是正确的,但是一旦我们进入Derived<Int>.method就错了。也许还有一些堆栈腐败正在发生?

对此代码进行的各种细微更改会使其正常工作(尽管语义不同)。谁能解释一下这里发生了什么?在我对Swift了解更多之前,我一直犹豫是否建议编译错误。

1 个答案:

答案 0 :(得分:1)

你肯定在编译器或运行时发现了一个错误 - 应该不可能得到像这样的段错写代码。它应该运行正常,或者无法编译。您甚至不需要容器类,可以使用

重现错误
let d: Base = Derived<Int>()
d.method(2)

但是,即使它是运行时中的一个错误,我也会说这可能不是一个好习惯。使派生类具有通用性的要求是有原因的,并且使用虚拟模板来捏造它可能不是一个好主意。例如,声明let d: Base = Derived<String>()是什么意思?

如果Base没有实际的实现并且只是抽象的(或者即使它是,并且您可以将它们排除在外),您可能最好用协议替换基础,也许使用关联的类型代表T:

protocol P {
    // instead of T, best to call it
    // something with a meaningful name...
    typealias T

    func method(input: Int) -> T
}

class D : P {
    typealias T = Int

    func method(input: Int) -> Int {
        return input * 2
    }
}

class Container<U: P> {
    let item:  U
    init(item: U) {
        self.item = item
    }
    func method(input: Int) -> U.T {
        return item.method(input)
    }
}

let d = D()
let c = Container(item: d)
c.method(2)

这种方法的另一个好处是允许D成为值类型(即结构),因为它们不能继承,但它们可以。