34

我一直在用 Swift 开发一个 iOS 应用程序(其中大部分是从 Objective-C 迁移而来的)。我正在使用 Core Data 并尝试使用扩展来向从我的模型自动生成的类添加功能。我在 Objective-C 中很容易做的一件事是在 A 类的一个类别中添加一个方法,并在 B 类(从 A 派生)的一个类别中覆盖该方法,我希望在 Swift 中做同样的事情。

一段时间以来,我的项目中有以下代码(这只是一个示例),虽然我还没有使用该功能,但编译器在编译这段代码时工作得很好:

// From CellType.swift -- NOTE: Imports from Foundation and CoreData
@objc(CellType)
class CellType: NSManagedObject {
    @NSManaged var maxUses: NSNumber
    @NSManaged var useCount: NSNumber
    // Other properties removed for brevity
}


// From SwitchCellType.swift -- NOTE: Imports from Foundation and CoreData
@objc(SwitchCellType)
class SwitchCellType: CellType {
    @NSManaged var targetCellXIndex: NSNumber
    @NSManaged var targetCellYIndex: NSNumber
    @NSManaged var targetCellType: CellType
    // Other properties removed for brevity
}


// From CellTypeLogic.swift -- NOTE: Imports from Foundation and CoreData
extension CellType
{
    var typeLabel : String { get { return "Empty"; } }
    func isEqualToType(otherCellType : CellType) -> Bool
    {
        return (self.typeLabel == otherCellType.typeLabel &&
            self.maxUses.isEqualToNumber(otherCellType.maxUses) &&
            self.useCount.isEqualToNumber(otherCellType.useCount));
    }
    // Code removed for brevity
}


// From SwitchCellTypeLogic.swift -- NOTE: Imports from Foundation and CoreData
extension SwitchCellType    // YES, this compiles with the overrides!
{
    override var typeLabel : String { get { return "Switch"; } }
    override func isEqualToType(otherCellType : CellType) -> Bool
    {
        var answer = false;
        if let otherSwitchCellType = otherCellType as? SwitchCellType
        {
            answer = super.isEqualToType(otherCellType) &&
                self.targetCellXIndex.isEqualToNumber(otherSwitchCellType.targetCellXIndex) &&
                self.targetCellYIndex.isEqualToNumber(otherSwitchCellType.targetCellYIndex) &&
                self.targetCellType.isEqualToType(otherSwitchCellType.targetCellType);
        }
        return answer;
    }
    // Code removed for brevity
}

希望那里的某个 Swift 专家已经看到了我的问题,但我是这样发现的:最近我尝试使用具有非内置类型的参数和/或返回值的方法添加类似的功能,但我开始得到这个错误:扩展中的声明还不能覆盖。

为了探讨这个问题,我将以下内容添加到我的一个 swift 文件中,认为它可以编译得很好:

class A
{
}

class B : A
{
}

extension A
{
    var y : String { get { return "YinA"; } }
}

extension B
{
    override var  y : String { get { return "YinB"; } }  // Compiler error (see below) -- What??
}

令我惊讶的是,我收到了相同的编译器错误(扩展中的声明还不能覆盖)。什么?但是我已经多次使用这种模式而没有编译器错误。

问题:首先,是否有一些关于在扩展中覆盖的规则,以便在某些情况下它应该工作,但在其他情况下却不是?第二(更令人不安)为什么 Swift 编译器看起来如此不一致?我在这里想念什么?请帮助我恢复对 Swift 的信心。

更新:

正如 Martin R 在正确答案中所指出的,您似乎可以覆盖当前版本的 Swift(1.1 通过 Xcode 6.1)中的方法,只要它们(1)仅涉及从 NSObject 派生的类并且(2)不使用 inout修饰符。这里有一些例子:

class A : NSObject { }

class B : A { }

class SubNSObject : NSObject {}
class NotSubbed {}
enum SomeEnum { case c1, c2; }

extension A
{
    var y : String { get { return "YinA"; } }
    func f() -> A { return A(); }
    func g(val: SubNSObject, test: Bool = false) { }

    func h(val: NotSubbed, test: Bool = false) { }
    func j(val: SomeEnum) { }
    func k(val: SubNSObject, inout test: Bool) { }
}

extension B 
{
    // THESE OVERIDES DO COMPILE:
    override var  y : String { get { return "YinB"; } }
    override func f() -> A { return A(); }
    override func g(val: SubNSObject, test: Bool) { }

    // THESE OVERIDES DO NOT COMPILE:
    //override func h(val: NotSubbed, test: Bool = false) { }
    //override func j(val: SomeEnum) { }
    //override func k(val: SubNSObject, inout test: Bool) { }

}
4

2 回答 2

38

似乎扩展中的重写方法和属性仅适用于与Objective-C 兼容的 方法和属性的当前 Swift (Swift 1.1/Xcode 6.1)。

如果一个类是从其派生的,NSObject那么它的所有成员在 Objective-C 中都自动可用(如果可能,请参见下文)。所以随着

class A : NSObject { }

您的示例代码按预期编译和工作。您的代码数据扩展覆盖了工作,因为NSManagedObject它是NSObject.

或者,您可以将 @objc 属性用于方法或属性:

class A { }

class B : A { }

extension A
{
    @objc var y : String { get { return "YinA" } }
}

extension B
{
   @objc override var y : String { get { return "YinB" } }
}

在 Objective-C 中不可表示的方法不能@objc 在子类扩展中被标记并且不能被覆盖。例如,这适用于具有inout参数或enum类型参数的方法。

于 2014-11-24T16:13:37.157 回答
0

我在 Xcode9 上经历过这个。关闭和重新打开 Xcode 对我有用。可能是编译器中的一个错误。

于 2017-10-03T08:03:35.413 回答