【问题标题】:Swift: Can I have a protocol that inherits from a protocol and constrains it?斯威夫特:我可以有一个从协议继承并约束它的协议吗?
【发布时间】:2019-10-29 08:21:10
【问题描述】:

假设我有以下协议:

protocol RateableItem {
   var identifier: String { get }  // placeholder. This could be a lot of properties
   var name: String { get set }
   var rating: Int { get set }
}

protocol RateableItemManager {

    /// get some objects matching query criteria
    func objects(matching query: RateableItemQuery) -> [RateableItem]

    /// get a specific object
    func object(withID identifier: String) -> RateableItem?

    /// persists them
    func save(_ object: RateableItem) throws

    /// deletes the objects.
    func delete(_ objects: [RateableItem])

    /// creates a new object.
    func create() -> RateableItem
}

struct RateableItemQuery {
    let searchPredicate: NSPredicate?  // nil means all
    let sortingBlock: ((RateableItem, RateableItem) throws -> Bool)?
    let groupingSpecifier: (() -> String)?
    init(matching predicate: NSPredicate? = nil,
         sort: ((RateableItem, RateableItem) throws -> Bool)? = nil,
         groupBy: (() -> String)? = nil) {

        self.searchPredicate = predicate
        self.sortingBlock = sort
        self.groupingSpecifier = groupBy
    }
}

我现在可以实现 this 的具体类型,它返回符合协议的具体类型。返回的具体类型与我的其余代码无关,因为其余代码只关心它们是否符合协议。这使我能够制作模型的“生产”和“虚拟”版本。

有没有办法可以更一般地定义这个,例如:

struct Query<T> {
    let searchPredicate: NSPredicate?  // nil means all
    let sortingBlock: ((T, T) throws -> Bool)?
    let groupingSpecifier: (() -> String)?
    init(matching predicate: NSPredicate? = nil,
         sort: ((T, T) throws -> Bool)? = nil,
         groupBy: (() -> String)? = nil) {

        self.searchPredicate = predicate
        self.sortingBlock = sort
        self.groupingSpecifier = groupBy
    }
}

这样

struct RateableItemQuery: Query<RateableItem> {}

protocol ItemManager<T> {

    func objects(matching query: Query<T>) -> [T]

    func object(withID identifier: String) -> T?

    func save(_ object: T) throws

    func delete(_ objects: [T])

    func create() -> T
}

protocol RateableItemManager: ItemManager<RateableItem>

因为我想使用这个 API 范例,但不一定想在“基本协议”级别限制任何东西,因为我经常只是为我想要的各种协议类型重写这些方法签名想合作。

如果我没记错的话,关联类型必须是具体的,使它们的返回类型也具体,然后我就不能轻松地使用协议类型。

对不起,如果我没有说“规范”。我希望我能够传达我的意图。

这可能是即将到来的 Swift 5.1 提供的不透明类型,返回 -> 一些 ProtocolType 吗?

【问题讨论】:

  • 不透明类型是您的答案。

标签: swift generics protocols swift-protocols


【解决方案1】:

所以事实证明,不透明类型也不是答案。

我通过为管理器创建抽象基类解决了我的问题,查询和查询结果是通用结构,管理器的具体子类可以获取和返回基于协议的数据类型。

public struct Query<T> {

    var searchPredicate: NSPredicate?
    var sortingBlock: ((T, T) throws -> Bool)?
    var groupingSpecifier: (() -> String)?

    var results: QueryResults<T>?

    init(matching predicate: NSPredicate?,
         sort: ((T, T) throws -> Bool)?,
         groupBy: (() -> String)?) {

    }
}


public struct QueryResults<T> {

    public enum ChangeType: UInt {
        case insert = 1
        case delete
        case move
        case update
    }

    public struct Section<T> {
        var items: [T]
        var title: String?
    }

    public var sections: [Section<T>] = []

    public func object(at indexPath: IndexPath) -> T? {
        return nil
    }
}

public class AnyObjectManager<ObjectType> {

    public enum Error: Swift.Error {
        case abstractImplementationRequiresOverride
    }

    typealias QueryDidChangeObjectBlock = ((
        _ query: Query<ObjectType>,
        _ didChangeObject: ObjectType,
        _ atPath: IndexPath?,
        _ forChangeType: QueryResults<ObjectType>.ChangeType,
        _ newIndexPath: IndexPath?) -> Void)

    typealias QueryDidChangeSectionBlock = ((
        _ query: Query<ObjectType>,
        _ didChangeSection: QueryResults<ObjectType>.Section<ObjectType>,
        _ atSectionIndex: Int,
        _ forChangeType: QueryResults<ObjectType>.ChangeType) -> Void)

    /// get some objects matching query criteria.  nil means return all
    func objects(matching query: Query<ObjectType>?) -> [ObjectType] {
        fatalError("Abstract implementation.  You need to override this method and provide an implementation!")
    }

    /// get a specific object
    func object(withID identifier: String) -> ObjectType? {
        fatalError("Abstract implementation.  You need to override this method and provide an implementation!")
    }

    /// deletes the objects.  Does it commit that to disk?
    func remove(_ objects: [ObjectType]) {
        fatalError("Abstract implementation.  You need to override this method and provide an implementation!")
    }

    /// creates a new object but does not save it.
    func create() -> ObjectType {
        fatalError("Abstract implementation.  You need to override this method and provide an implementation!")
    }

    /// this is basically to mimic the functionality of a fetched results controller...
    func monitorQuery(_ query: Query<ObjectType>,
                      willChangeBlock: ((_ query: Query<ObjectType>) -> Void)?,
                      didChangeObjectBlock: QueryDidChangeObjectBlock?,
                      didChangeSectionBlock: QueryDidChangeSectionBlock?,
                      didFinishChangesBlock:((_ query: Query<ObjectType>) -> Void)?) {
        fatalError("Abstract implementation.  You need to override this method and provide an implementation!")
    }
    /// and this is to stop monitoring that.
    func stopMonitoringQuery() {
        fatalError("Abstract implementation.  You need to override this method and provide an implementation!")
    }

    public func saveChanges() throws {
        fatalError("Abstract implementation.  You need to override this method and provide an implementation!")
    }

    public func discardChanges() throws {
        fatalError("Abstract implementation.  You need to override this method and provide an implementation!")
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-04-25
    • 1970-01-01
    • 2018-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多