重新实现在协议扩展中添加的功能的调用

时间:2018-11-15 22:28:39

标签: swift

我为协议添加了协议扩展,其关联类型为PickerType。我写了一个函数refresh(:,completion:)的重新实现,该函数在协议中定义并在其他协议扩展中实现。

但是,除非编译器知道refresh(:,completion:)是什么类型,否则我在调用PickerType时不会调用新扩展名内的函数。我写了以下内容:

extension PickerItemProvider where PickerType: Equatable & SyncableEntity {
    func refresh(_ sender: Any, completion: (() -> Void)?) {
        print("we're trying to have this implementation called")
        PickerType.startSync()
    }
}

如果我在refresh(:,completion:)上调用PickerSectionProvider<ObservationType>(如下面的Playground中的代码所示),就会像我期望的那样被调用,但是当我在{{1}上调用refresh(:,completion:)时却不会},这是一种通用类型,必须符合ItemProvider(再次参见下面的代码)。

PickerItemProvider

2 个答案:

答案 0 :(得分:2)

请参阅以下更正的实现,

import Foundation

protocol PickerItemProvider: class {
    associatedtype PickerType
    func findItem(by identifier: NSNumber) -> PickerType?
    func itemAt(_ indexPath: IndexPath) -> PickerType?
}

extension PickerItemProvider {
    func findItem(by identifier: NSNumber) -> PickerType? {
        return nil
    }

    func refresh(_ sender: Any, completion: (() -> Void)?) {
        print("the default refresh implementation")
    }
}

public class PickerSectionProvider<ProvidedType: Equatable> : PickerItemProvider {
    func itemAt(_ indexPath: IndexPath) -> ProvidedType? {
        return nil
    }
}

extension PickerItemProvider where PickerType: Equatable & SyncableEntity {

    func refresh(_ sender: Any, completion: (() -> Void)?) {
        print("we’re trying to have this implementation called instead of the above implementation of refresh")
        PickerType.startSync()
    }
}

protocol SyncableEntity {
    static func startSync()
}

extension SyncableEntity {
    static func startSync() {

    }
}

class ObservationType: Equatable, SyncableEntity {

}

func ==(lhs: ObservationType, rhs: ObservationType) -> Bool {
    return false
}

class GenericPickerViewController<PickerType: Equatable & SyncableEntity, ItemProvider: PickerItemProvider> where ItemProvider.PickerType == PickerType {
    var itemProvider: ItemProvider?

    init() {

    }

    func foo() {
        // Why doesn’t the implementation of refresh(:,completion:) we added get called here?
        itemProvider?.refresh("dummy sender") {

        }
    }
}

class PopupPickerRow<T: Equatable & SyncableEntity, ItemProvider: PickerItemProvider> where ItemProvider.PickerType == T {
    var pickerController = GenericPickerViewController<T, ItemProvider>()
}

let pickerSectionProvider = PickerSectionProvider<ObservationType>()

let row = PopupPickerRow<ObservationType, PickerSectionProvider<ObservationType>>()

row.pickerController.itemProvider = pickerSectionProvider

row.pickerController.foo()

首先,当您想overridemethod中声明为protocol的实现,并在protocol {{1}中提供了一些默认实现时},它将始终调用extension中实现的方法,并丢弃extension子句要求。您可以在this问题和this中看到类似的问题。

因此,要使where寻找protocol来满足method子句中的约束,您需要从where除去method签名并保持就像我上面所做的那样,它只在protocol内部。

第二,您在定义extensionPickerType时错过了Equatable & SyncableEntityGenericPickerViewController的要求,因此我也对此进行了更新。

答案 1 :(得分:2)

我在GenericPickerViewController上添加了两个功能,一个用于通过PickerItemProvider完全符合PickerType的{​​{1}}对其进行设置,另一个使用{ {1}符合Equatable的{​​1}}。这样,编译器便知道要调用哪个PickerItemProvider。这是Playground代码:

PickerType