简体   繁体   English

Swift泛型与协议

[英]Swift Generics & Protocols

I'm trying to build a generic 'NSFetchResultsController'-like set of protocols in swift so that I might isolate my UITableViewDataSource/UICollectionViewDataSource implementations from any specifics about where and how the data is being sourced and updated. 我正在尝试快速构建通用的类似于“ NSFetchResultsController”的协议集,以便可以将UITableViewDataSource/UICollectionViewDataSource实现与任何有关在何处以及如何获取和更新数据的细节隔离开来。

So I began with a couple of simple definitions of the core controller and sections thus: 因此,我首先对核心控制器及其部分进行了一些简单的定义:

import UIKit
import CoreData

public protocol ResultsSection {
    typealias T

    var numberOfObjects : Int { get }
    var objects : [T] { get }

    subscript(index: Int) -> T? { get }
}

public protocol ResultsController {

    typealias ResultsSection
    typealias T

    var resultsSections : [ResultsSection] { get }

    subscript(indexPath: NSIndexPath) -> T? { get }
}

Following this a couple of simple implementations just holding everything in arrays thus: 在此之后的几个简单的实现仅将所有内容保存在数组中:

class SimpleResultsSection<T> : ResultsSection {

    private(set) var objects : [T]

    init(objects: [T]?) {
        self.objects = objects ?? [T]()
    }

    var numberOfObjects : Int {
        get {
            return objects.count
        }
    }

    subscript(index: Int) -> T? {

        return objects.count > index ? objects[index] : nil
    }
}

class SimpleResultsController<T, RS: ResultsSection where T == RS.T> : ResultsController {

    internal(set) var resultsSections = [RS]()

    subscript(indexPath: NSIndexPath) -> T? {

        if resultsSections.count > indexPath.section {
            let section = resultsSections[indexPath.section]
            return section[indexPath.row]
        }
        else {
            return nil
        }
    }

    init(singleSectionObjects: [T]) {
        let section = SimpleResultsSection(objects: singleSectionObjects)
        resultsSections.append(section)
    }
}

Now on the last line of code where I try to append a SimpleResultsSection<T> to self.resultsSections the compiler denies me with: 现在在代码的最后一行,我尝试将SimpleResultsSection<T>追加到self.resultsSections ,编译器拒绝我执行以下操作:

Cannot invoke 'append' with an argument list of type (SimpleResultsSection<T>)

What have I missed here? 我在这里错过了什么?

I thought that <T, RS: ResultsSection where T == RS.T> would mean that the compiler would be able to resolve SimpleResultsSection<T> to an RS and thus allow me to append a SimpleResultsSection<T> to an [RS] , but clearly I'm missing something. 我认为<T, RS: ResultsSection where T == RS.T>意味着编译器将能够将SimpleResultsSection<T>解析为RS ,从而允许我append SimpleResultsSection<T> append[RS] ,但显然我缺少了一些东西。

You're not making as strong-enough promise here: 您在这里没有做出足够强的承诺:

internal(set) var resultsSections = [RS]()

This only promises that the array is full of ResultsSection where T == RS.T , but that could be a completely unrelated class. 这只能保证数组充满了ResultsSection where T == RS.T ,但这可能是一个完全不相关的类。 Swift arrays are not covariant in that way. Swift数组不是以这种方式协变的。 If they were, you to treat [Apple] as [Fruit] and then append(orange) . 如果是,则将[Apple]视为[Fruit] ,然后append(orange)

What you want here is: 您想要的是:

internal(set) var resultsSections = [SimpleResultsSection<T>]()

That's a stronger promise that all of the elements inherit from the same class, while still respecting your earlier protocol promise that it could be read as [ResultSection] . 这是对所有元素都继承自同一类的有力保证,同时仍然尊重您先前的协议,即可以将其读取为[ResultSection]

That said, I would do it a bit differently. 也就是说,我会做一些不同的事情。 Unless you really need SimpleResultsController to be able to accept multiple types of sections, I would force that with a typealias rather than parameterizing it: 除非您确实需要SimpleResultsController能够接受多种类型的节,否则我将使用typealias而不是对其进行参数化:

class SimpleResultsController<T> : ResultsController {
    internal(set) var resultsSections = [SimpleResultsSection<T>]()
    ...

This way, the type is SimpleResultsController<Int> rather than SimpleResultsController<Int, SimpleResultsSection<Int>> (which is a very cumbersome type). 这样,类型是SimpleResultsController<Int>而不是SimpleResultsController<Int, SimpleResultsSection<Int>> (这是非常麻烦的类型)。

The system can infer the type of ResultsSection from the definition of resultsSection , so there's no need to typealias it if you don't want to (though it can be handy to do that for clarity). 该系统可以推断出的类型ResultsSection从定义resultsSection ,所以没有必要typealias它,如果你不希望(尽管它可以很方便做,为清楚)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM