Swift中的"open"关键字和可在扩展中重写的方法/属性?

13

随着Swift 3.0中引入open关键字(Swift中的'open'关键字是什么?),扩展NSObject派生类或@objc属性/方法的限制。

在模块/框架之间声明和使用扩展public (class) 方法/属性的代码已经失效,因为public 不再意味着在定义模块之外可以被重写。

例如:

public extension UIManagedDocument {

    public class func primaryDocumentName() -> String {
        return "Document"
    }

    public class func primaryStoreURL() -> URL {
        let documentsURL = FileManager.default.userDocumentsURL
        return URL(fileURLWithPath: self.primaryDocumentName(), isDirectory: false, relativeTo: documentsURL)
    }

    public class func primaryModelName() -> String? {
        return "Model"
    }

}
  • 原始提案(SE-0117)侧重于子类化,没有提到扩展。
  • 目前,扩展不支持open关键字(您无法编写open extension NSObject以及open func Method())。

问题: 有没有解决办法可以跨模块/框架覆盖扩展提供的方法/属性?


这真的与新的开放 vs 公共访问模式有关吗?除非我弄错了,在 Swift 2 和 Swift 3 中都无法覆盖在扩展中声明的方法。 - Martin R
你说得没错,对于“纯”的Swift类来说是这样的,但是我们也可以使用继承自NSObject的类,并且可以使用带有@objc属性的方法/属性。 (Swift中是否可以在扩展之间进行重写? - Nocross
我明白了,谢谢。也许你可以把那个信息添加到问题中。 - Martin R
谢谢你提醒我我的答案 :) - Martin R
2个回答

9

除非我错了,否则在您的框架中,您可以将扩展方法声明为open,如果您只省略扩展声明中的public关键字。

extension UIManagedDocument {

    open class func primaryDocumentName() -> String {
        return "Document"
    }
    // ...
}

然后(对于NSObject子类或@objc成员),您可以在主应用程序(或任何模块)中覆盖自定义子类中的方法:

class MyManagedDocument: UIManagedDocument {

    override class func primaryDocumentName() -> String {
        return "MyDocument"
    }
    // ...
}

我不能省略 public 关键字,因为扩展是在另一个模块(共享框架)中声明的,具体的子类是在依赖目标(应用程序/扩展)中实现的。 - Nocross
@Nocross:实际上我刚刚测试了一下,对我来说它是有效的。extension UIManagedDocument {}在一个框架中,而class MyManagedDocument: UIManagedDocument {}则在主应用程序中。 - Martin R
你说得对,它确实可以工作。但是让我困扰的是,这种方式依赖于Swift的“推断”逻辑,这至少是不稳定的,而且可能会因为不同的IDE而有所不同。 - Nocross
@Nocross:我认为这与IDE无关。请注意,您在解决方案中也删除了public关键字。 - Martin R
一个细微之处有所不同。我省略了它,因为协议一致性扩展也符合协议中的访问修饰符。 - Nocross
@Nocross:据我了解,扩展的默认访问级别是类本身的访问级别(在这种情况下为:公开)。 - Martin R

3
  • “面向协议”-声明具有所需方法/属性的协议(protocol),然后重构您的扩展以符合该协议(protocol)
  • “传统方式”-实现具有所需方法/属性的中间(抽象)子类。

协议示例:

protocol PrimaryDocument {
    static func primaryDocumentName() -> String

    static func primaryStoreURL() -> URL

    static func primaryModelName() -> String?
}

extension UIManagedDocument : PrimaryDocument {

    open class func primaryDocumentName() -> String {
        return "Document"
    }

    open class func primaryStoreURL() -> URL {
        let documentsURL = FileManager.default.userDocumentsURL
        return URL(fileURLWithPath: self.primaryDocumentName(), isDirectory: false, relativeTo: documentsURL)
    }

    open class func primaryModelName() -> String? {
        return "Model"
    }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接