符合协议要求

3
我正在尝试一些Swift特性(集合协议)。我想实现一个实现CollectionType的结构体。我的代码如下:
 struct Book {
    let id : String
    let name : String

    init(id: String, name: String) {
        self.id = id
        self.name = name
    }
}
struct BookCollection {
    var books : [Book]
}

首次尝试:

extension BookCollection : CollectionType {
}

编译器错误: "类型BookCollection不符合协议Indexable"

好的,让我们遵循Indexable协议:

extension BookCollection : CollectionType {
    var startIndex: Int {return 0}
    var endIndex: Int {return books.count}

    subscript(idx: Int) -> Book {
        return books[idx]
    }
}

代码现在已经能够运行。

我的问题是:如果忽略编译错误,有没有一种简单的方法可以知道在Swift中遵循协议时应该实现哪些函数/属性?谢谢。

PS:我不想读所有的扩展,以查看是否有默认实现,这太痛苦了。


1
按住 Command 键并单击 CollectionType 可以跳转到协议。任何不是 optional 的内容都应该被实现。 - hannad
在协议定义中,有些被标记为@optional。这意味着任何想要遵循该协议的类,可以选择性地实现这个函数。任何没有在其前面加上@optional的函数或属性都必须被实现,以避免代码产生编译错误。 - hannad
@hannad,你在哪里看到了"@optional"关键字? - samir
@samir 如果你正在使用Xcode,那么在代码中Command + 点击 CollectionType,将会为你打开代码文件,以显示该协议的属性和方法。 - hannad
当您使用Xcode时,是否看到了一些带有“@optional”标记的函数,就像您所说的那样?如果是的话,您使用的是哪个版本的Xcode?谢谢。 - samir
显示剩余7条评论
2个回答

1
编译器错误会告诉你。我把你的代码放在游乐场里,看到了以下错误。
Playground execution failed: MyPlayground.playground:15:1: error: type 'BookCollection' does not conform to protocol 'Indexable'
extension BookCollection: CollectionType
^
Swift.Indexable:6:15: note: unable to infer associated type 'Index' for protocol 'Indexable'
    typealias Index : ForwardIndexType
              ^
Swift.CollectionType:2:12: note: inferred type 'Range<BookCollection.Index>' (by matching requirement 'subscript') is invalid: does not conform to 'ForwardIndexType'
   public subscript (bounds: Range<Self.Index>) -> Slice<Self> { get }
           ^
MyPlayground.playground:15:1: error: type 'BookCollection' does not conform to protocol 'SequenceType'
extension BookCollection: CollectionType
^
Swift.SequenceType:35:17: note: protocol requires function 'generate()' with type '() -> Generator'
    public func generate() -> Self.Generator
                ^
Swift.SequenceType:2:17: note: candidate has non-matching type '<`Self`> () -> `Self`' (aka '<τ_0_0> () -> τ_0_0')
    public func generate() -> Self
                ^
Swift.SequenceType:5:17: note: candidate has non-matching type '<`Self`> () -> `Self`.Base.Generator' (aka '<τ_0_0> () -> τ_0_0.Base.Generator')
    public func generate() -> Self.Generator
                ^
Swift.CollectionType:2:17: note: candidate has non-matching type '<`Self`> () -> IndexingGenerator<`Self`>' (aka '<τ_0_0> () -> IndexingGenerator<τ_0_0>')
    public func generate() -> IndexingGenerator<Self>

从头开始看,我发现我们需要一个与索引类型相关联的类型,然后有很多错误与它没有定义有关,所以我在扩展中为协议Indexable添加了一个typealias,这给了我更多的错误,所以我暂时将CollectionType更改为Indexable。
extension BookCollection: Indexable
{
    typealias Index = Int
}

现在我有以下内容:
Playground execution failed: MyPlayground.playground:15:1: error: type 'BookCollection' does not conform to protocol 'Indexable'
extension BookCollection: Indexable
^
Swift.Indexable:21:15: note: protocol requires nested type '_Element'
    typealias _Element
              ^

_Element应该是一项实现细节(它以下划线开头),但现在让我们使用它并为其添加一个类型别名。

extension BookCollection: Indexable
{
    typealias Index = Int
    typealias _Element = Book
}

现在我有以下内容。
Playground execution failed: MyPlayground.playground:15:1: error: type 'BookCollection' does not conform to protocol 'Indexable'
extension BookCollection: Indexable
^
Swift.Indexable:12:16: note: protocol requires property 'startIndex' with type 'Index' (aka 'Int')
    public var startIndex: Self.Index { get }
               ^
Swift.Indexable:20:16: note: protocol requires property 'endIndex' with type 'Index' (aka 'Int')
    public var endIndex: Self.Index { get }
               ^
Swift.Indexable:22:12: note: protocol requires subscript with type 'Index -> _Element'
    public subscript (position: Self.Index) -> Self._Element { get }

现在我实现了两个属性和下标。我注意到实现下标可以让编译器推断出这两个类型别名,所以我可以删除那些声明。

extension BookCollection: Indexable
{
    var startIndex: Int { return books.startIndex }
    var endIndex: Int { return books.endIndex }

    subscript(index: Int) -> Book
    {
        return books[index]
    }
}

这段代码没有错误,所以我们将协议改回CollectionType,但仍然没有错误,说明我们已经完成了。

谢谢你的回答。我发现这种方法很痛苦。我希望在未来的Swift版本中能看到其他方法(比如Objective C中的@optional关键字)。 - samir
我花了大约5分钟来浏览所有内容。可选注释的问题(确实存在)是,一些不是可选的方法确实具有默认实现,这是CollectionType的情况。有很多非可选方法,但如果你实现了“Indexable”,就不必实现它们。 - JeremyP

0

除了使用编译器报错之外,没有其他简单的方法。为什么?因为协议支持默认实现,你无法知道哪些协议函数有默认实现(除非编译器不对它们进行投诉)。

例如,SequenceType 定义了许多函数。你要全部实现吗?不需要,因为大多数都有默认实现。唯一需要的是 func generate() -> GeneratorType 和相关的 GeneratorType。之后,你的 SequenceType 子类型就完成了——你可以选择覆盖一些默认实现,但不需要这样做。对于 CollectionType,它是 Indexable(而 GeneratorType 默认为 IndexingGenerator<Self>)。

当然,如果你有源代码,那么你可以识别默认的协议实现,但是在你完成这个过程时,你可能会让编译器告诉你。或者,苹果文档列出了一些带有“默认实现”的函数,这可能表明哪些不需要实现。或者,如评论中所述,swiftdoc.org 以清晰的形式标识所需的类型/函数。


1
我认为即使不查看源代码,swiftdoc.org也提供了非常好的概述,说明在符合协议时必须提供哪些方法或属性。在您上面的示例中,swiftdoc对SequenceType的描述清楚地显示,只有函数generate()(注意,不是generator())需要符合协议。 - dfrib
1
@dfri 函数 generate() 返回 IndexingGenerator<Self> 类型的生成器,具有默认实现 :-) (是的,它是必需的,但也已经实现了...) - user3441734

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