【问题标题】:reimplementation of a function added in a protocol extension isn’t called不调用在协议扩展中添加的函数的重新实现
【发布时间】:2018-11-15 22:28:38
【问题描述】:

我为一个协议添加了一个协议扩展,它的关联类型称为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()
    }
}

如果我在 PickerSectionProvider<ObservationType> 上调用 refresh(:,completion:)(请参阅下面我的 Playground 中的代码),它会像我预期的那样被调用,但当我在 ItemProvider 上调用 refresh(:,completion:) 时不会调用,这是一个通用类型必须符合PickerItemProvider(再次参见下面的代码)。

// We are trying to get our pickers to sync their provided type when you pull to refresh. But the implementation of refresh(:, completion:) that we added doesn’t get called.

import Foundation

protocol PickerItemProvider: class {
    associatedtype PickerType
    func findItem(by identifier: NSNumber) -> PickerType?
    func itemAt(_ indexPath: IndexPath) -> PickerType?
    func refresh(_ sender: Any, completion: (() -> Void)?)
}

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

    public 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, 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, 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()

【问题讨论】:

    标签: swift


    【解决方案1】:

    请参阅下面的更正实施,

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

    首先,当你想override一个method实现在protocol中声明为一个要求并在protocol中提供了一些默认实现extension它总是会调用在extension中实现的方法extension 放弃 where 子句要求。您可以在this question 和this 中看到类似的问题。

    因此,要使protocol 查找满足where 子句中的约束的method,您需要像我一样从protocol 中删除method 签名并仅将其保留在extension 内以上。

    其次,您在定义GenericPickerViewControllerPopupPickerRow 时错过了PickerType 要求为Equatable &amp; SyncableEntity,所以我也更新了它。

    【讨论】:

    • 感谢您的回复,但我们正努力让 PopupPickerRowGenericPickerViewController 与任何东西一起工作,只要它是平等的,而不是仅限于 Equatable &amp; SyncableEntity。如果我删除您所做的更改,问题仍然存在。
    • 是的,但是您调用该方法实现的protocol 要求将中断,它将调用默认方法而不考虑任何约束。我希望你明白你的实现出了什么问题,所以这取决于你想如何处理它。
    • 我想我开始明白了。所以在编译时会选择refresh(:,completion:) 的默认实现,因为关于 itemProvider 的所有已知信息是它属于符合 PickerItemProvider 的类型,其中 PickerType 是 Equatable?
    • 所以我在GenericPickerViewController 上添加了一个扩展,其中PickerType 符合EquatableSyncableEntity,它添加了foo() 的不同实现。在该扩展中调用 itemProvider 上的 refresh(:,completion:) 会导致调用我添加的 refresh(:,completion:) 实现。
    • 这在 Playground 中有效,但在我的项目中无效。我猜这是因为编译真正的 foo() 时,编译器不知道 PickerType 将是什么类型,所以它选择了默认类型,而不是我添加的扩展中的类型。
    【解决方案2】:

    我在GenericPickerViewController 上添加了两个函数,一个用于设置PickerItemProvider,其PickerType 仅符合Equatable,另一个用于设置PickerItemProvider,其PickerType 符合SyncableEntity。这样编译器就知道要调用哪个refresh(:,completion:)。这是游乐场代码:

    import Foundation
    import CoreData
    
    protocol PickerItemProvider: class {
        associatedtype PickerType
        func itemAt(_ indexPath: IndexPath) -> PickerType?
    }
    
    extension PickerItemProvider {
        public 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()
            completion?()
        }
    }
    
    protocol SyncableEntity {
        associatedtype EntityType
        static func startSync()
    }
    
    extension SyncableEntity {
        static func startSync() {
            print("starting sync")
        }
    }
    
    class ObservationType: Equatable, SyncableEntity {
        typealias EntityType = NSManagedObject
    }
    
    func ==(lhs: ObservationType, rhs: ObservationType) -> Bool {
        return false
    }
    
    class GeneralPickerViewController<PickerType: Equatable, ItemProvider: PickerItemProvider> where ItemProvider.PickerType == PickerType {
        private var itemProvider: ItemProvider?
    
        private var refresher: ((Any, (() -> ())?) -> ())?
    
        func setup<T: PickerItemProvider>(with itemProvider: T) where T.PickerType == PickerType {
            refresher = { sender, completion in
                itemProvider.refresh(self, completion: {
                    completion?()
                })
            }
            self.itemProvider = (itemProvider as! ItemProvider)
        }
    
        func setup<T: PickerItemProvider>(with itemProvider: T) where T.PickerType == PickerType, PickerType: SyncableEntity {
            refresher = { sender, completion in
                itemProvider.refresh(self, completion: {
                    completion?()
                })
            }
            self.itemProvider = (itemProvider as! ItemProvider)
        }
    
        func foo() {
            refresher?(self, {
                print("finished")
            })
        }
    }
    
    class PopupPickerRow<T: Equatable, ItemProvider: PickerItemProvider> where ItemProvider.PickerType == T {
        var pickerController = GeneralPickerViewController<T, ItemProvider>()
    }
    
    let pickerSectionProvider = PickerSectionProvider<ObservationType>()
    
    let row = PopupPickerRow<ObservationType, PickerSectionProvider<ObservationType>>()
    
    row.pickerController.setup(with: pickerSectionProvider)
    
    row.pickerController.foo()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多