3

此代码会生成 Xcode 错误消息,引导您转圈。假设我有一个称为协议的协议Marker,我希望标记能够复制自己。这是第一个猜测...

protocol Marker {
    func copy() -> Self
}
class Marker1 : Marker {
    func copy() -> Self {
        return Marker1()   // error here
    }
}

(我不确定如何正确使用,因为我在The Swift Programming LanguageSelf文档中找不到它。如果您知道它在哪里记录,请在答案中包含它。)

该代码在标记的行上给出了错误:Cannot convert return expression of type 'Marker1' to return type 'Self'并建议修复:Insert ' as! Self'

我接受修复:

...
    return Marker1() as! Self
...

这会导致另一个编译器错误:'Self' is only available in a protocol or as the result of a method in a class; did you mean 'Marker1'?

如果我接受那个“修复”,它会回到原来的错误。我将其称为 Xcode 中的错误。让我们试试别的东西:

func copy() -> Marker1 {
    return Marker1()
}

另一个错误:Method 'copy()' in non-final class 'Marker1' must return `Self` to conform to protocol 'Marker'

制作课程final确实修复了错误。但是有没有办法做到这一点而不使班级决赛?记录在哪里Self

4

2 回答 2

4

使用这样的层次结构,您必须使类符合协议final

protocol Marker {
    func copy() -> Self
}
final class Marker1 : Marker {
    func copy() -> Marker1 {
        return Marker1()
    }
}

之所以需要,final是因为当您不申请final并创建子类时Marker2: Marker1,复制将不再返回正确的类Self

您可以通过创建required初始化程序并始终创建正确的实例来解决此问题:

protocol Marker {
    init()
    func copy() -> Self
}
class Marker1 : Marker {
    required init() {
    }
    func copy() -> Self {
        let copy = type(of: self).init()
        return copy
    }
}

(由于不起作用,删除了原始代码)

相关:在 Swift 中实现 copy()

于 2018-04-12T15:34:14.467 回答
1

您当前实现的问题在于它Self是一种抽象类型,因此您不能简单地返回具体类型,例如Marker1从返回类型为Self. 如果将返回类型更改为Marker1,则需要将类设为 final 以确保没有子类可以覆盖该方法,因为在子类中,Self将对应于子类类型。但是,这仍然不好,因为在这种情况下,子类将不符合协议,因为在 的子类中Marker1,返回类型Marker1与 不同self

您可以通过使用 获取当前类的元类型type(of: self),然后在元类型上调用指定的初始化程序来解决此问题,这确实会返回 type 的实例Self。最后一步是为您的类创建一个必需的初始化程序,以确保所有子类都需要实现在您的copy()方法中调用的相同初始化程序,以便也copy为子类工作。

protocol Marker {
    func copy() -> Self
}

class Marker1 : Marker {
    func copy() -> Self {
        let result = type(of: self).init()
        return result
    }

    required init() {}
}
于 2018-04-12T15:40:08.650 回答