Swift Generics & Protocols(英语:Swift Generics & Protocols)



我正在尝试在swift中构建一组通用的"NSFetchResultsController"协议,以便我可以将我的UITableViewDataSource/UICollectionViewDataSource实现与有关数据来源和更新的位置和方式的任何细节隔离开来。

因此,我从核心控制器的几个简单定义开始,并按以下部分开始:

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 }
}

在此之后,几个简单的实现只是将所有内容保存在数组中:

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)
    }
}

现在在我尝试附加SimpleResultsSection<T>的最后一行代码中,编译器拒绝了我self.resultsSections

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

我在这里错过了什么?

我认为<T, RS: ResultsSection where T == RS.T>意味着编译器将能够将SimpleResultsSection<T>解析为RS,从而允许我将SimpleResultsSection<T> append[RS],但显然我错过了一些东西。

你在这里没有做出足够有力的承诺:

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

这只承诺数组充满了ResultsSection where T == RS.T,但这可能是一个完全不相关的类。 Swift 数组不是以这种方式协变的。如果是,您将[Apple]视为[Fruit]然后append(orange).

你在这里想要的是:

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

这是一个更强大的承诺,所有元素都继承自同一个类,同时仍然尊重你之前的协议承诺,它可以被解读为[ResultSection]

也就是说,我会做得有点不同。除非你真的需要SimpleResultsController才能接受多种类型的部分,否则我会用类型别名来强制它,而不是参数化它:

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

这样,类型是SimpleResultsController<Int>而不是SimpleResultsController<Int, SimpleResultsSection<Int>>(这是一个非常繁琐的类型)。

系统可以从resultsSection的定义中推断出ResultsSection的类型,因此如果您不想typealias,则无需(尽管为了清楚起见,这样做可能很方便)。

最新更新