【问题标题】:Unable to call initializer for subclass of generic type无法为泛型类型的子类调用初始化程序
【发布时间】:2014-11-29 03:31:49
【问题描述】:

更新:问题和标题已根据针对最初问题所做的更改进行了重述。


我有一个基类,它实现了用于创建新实例的通用类方法。该类方法的简化逻辑如下

class MyBaseClass {

    required init(_ record:MyRecordType) {
        println("Entered MyBaseClass.init")
        // Initialize base class from record
    }

    class func retrieveInstance<T:MyBaseClass>(name:String, callback:(T) -> ()) {
        let myRecord:MyRecordType = ... // Perform fetch to get a record for name
        let result = (T.self as T.Type)(myRecord) // Code currently crashes with BAD_ACCESS at this line
        callback(result)
    }
}

然后我用如下逻辑实现这个基类的子类

class MySubClass : MyBaseClass {

    required init(_ record:MyRecordType) {
        println("Entered MySubClass.init")
        // Initialize subclass from record
        super.init(record)
    }
}

接下来,我尝试调用泛型类方法

class AnotherClass {
    func successCallback(result:MySubclass) {
        // Handle callback
    }
    MySubClass.retrieveInstance("objectName", callback:successCallback)
}

在创建类的实例时 - 标有注释的行标识了崩溃位置 - 我期待调用来自 MySubClass 的 init 方法。 (这个奇怪的符号是基于回复中建议的错误解决方法)

此代码没有为MySubClass 调用init 方法,而是因BAD_ACCESS 而崩溃。我找不到任何 nil 引用或任何其他可以解释此崩溃的内容。

【问题讨论】:

  • 我无法重现 BAD_ACCESS。我假设您正在使用一些异步调用,也许问题就在那里。
  • @rintaro - 我终于能够隔离问题了。事实证明它与异步无关,而是涉及将重写的初始化程序与所需的初始化程序混合(我将在下面发布完整描述)。由于您的回答至少让我指出了正确的方向,所以我想在这里给您正确的答案,但您似乎已经删除了您的答案。如果您希望我感谢您的帮助,请随时重新发布。

标签: swift generics


【解决方案1】:

在@rintaro 最初发布的答案的帮助下,我能够解决这个问题,尽管我将在一个单独的问题下发布一个奇怪的问题。

事实证明,@rintaro 使用以下语法初始化泛型类型的实例是绝对正确的:

let result = (T.self as T.Type)(myRecord)

只要基类MyBaseClassrequired 标签声明了这个初始化器,这就可以工作:

public class MyBaseClass {
    public required init(_ record:MyRecordType) {
        ...
    }
}

子类MySubClass实现了匹配的初始化器:

public class MySubClass : MyBaseClass {
    public required init (_ record:MyRecordType) {
        ...
        super.init(record)
    }
}

然而,当我实际上有一个 3 级类层次结构和初始化器层次结构通过 override 混合时,事情就失败了。为了设想这一点,请考虑一组表示树结构中节点的类:

public class Node {
    public init(_ record:MyRecordType) {
       ...
    }
}

public class RootNode : Node {
    override public init(_ record:MyRecordType) {
        ...
        super.init(record)
    }
    public class func <T:RootNode>retrieveAll(success:(T) -> ()) {
        // Retrieve all instances of root node subclass T, and invoke success callback with new T instance
    }
}

public class ChildNode : Node {
    public init(_ record:MyRecordType, parentNode:Node) {
        ...
        super.init(record)
    }
    public class func <T:ChildNode>retrieveChildren(parent:Node, success:(T) -> ()) {
        // Retrieve all child T instances of parent node, and invoke success callback with new T instance
    {
}

问题出现在RootNode类的retrieveAll方法的实现中。为了让它按照@rintaro 的描述工作,我需要用RootNode 中的init 标记required 关键字。但是因为它还覆盖了来自Node 的初始化器,所以它还需要有override 关键字。所以我尝试在声明中使用这两个关键字:

override required public init(_ record:MyRecordType) {
    ...
}

Swift 编译器接受这一点,但是当我使用它从 retrieveAll 方法中初始化一个实例时,它会因 BAD_ACCESS 而崩溃。

我可以通过稍微更改NodeClass 的方法签名来解决这个问题,这样它的RootNode 子类就不需要覆盖:

public class Node {
    public init(record:MyRecordType) {
        ...
    }
}
public class RootNode {
    public required init(_ record:MyRecordType) {
        ...
        super.init(record:record)
    }
}

通过这种变通方法,RootNode 的子类可以正确实现所需的初始化程序,RootNode 中的retrieveAll 方法可以正确实例化这些子类的实例。

【讨论】:

  • 在基类和子类上添加所需的初始化程序后,您可以使用T()
  • @seanwoodward - 不幸的是,这似乎不是真的。当我使用 T(myRecord) 调用初始化程序时,它失败了,当我按照 rintaro 建议的方式调用它时它会失败。 T(myRecord) 的失败似乎是因为在获取实例的“记录”属性时出现 BAD_ACCESS 异常,该属性应该设置为传入的“myRecord”的值。
  • 感谢您的关注。对通用初始化程序 T() 的调用在简单测试中对我有用。我希望这些问题可以在编译器的未来版本中得到解决。
  • 据我所知,init 函数必须具有“必需”说明符,否则它将不起作用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多