【问题标题】:Ambiguous functions in multiple protocol extensions?多个协议扩展中的模糊功能?
【发布时间】:2016-04-25 13:02:01
【问题描述】:

我有多个具有相同函数名称的协议。有些协议有关联的类型,我不知道如何像在非泛型协议中那样调用函数。我得到错误:Protocol 'MyProtocol1' can only be used as a generic contraint because it has Self or associated type requirements

这就是我想要做的:

protocol Serviceable {
   associatedtype DataType
   func get(handler: ([DataType] -> Void)?)
}

struct PostService: Serviceable {
   func get(handler: ([String] -> Void)? = nil) {
      print("Do something...")
   }
}

protocol MyProtocol1: class {
   associatedtype ServiceType: Serviceable
   var service: ServiceType { get }
}

extension MyProtocol1 {
   func didLoad(delegate: Self) {
      print("MyProtocol1.didLoad()")
   }
}

protocol MyProtocol2: class {

}

extension MyProtocol2 {
   func didLoad(delegate: MyProtocol2) {
      print("MyProtocol2.didLoad()")
   }
}

class MyViewController: UIViewController, MyProtocol1, MyProtocol2 {
   let service = PostService()

   override func viewDidLoad() {
      super.viewDidLoad()
      didLoad(self as MyProtocol1) // Error here: Protocol 'MyProtocol1' can only be used as a generic contraint because it has Self or associated type requirements
      didLoad(self as MyProtocol2)
   }
}

如何从通用协议扩展中专门调用该函数?

【问题讨论】:

    标签: swift generics swift2 protocols protocol-extension


    【解决方案1】:

    通过将协议转换为通用协议(见下文)或为这些协议创建type eraser 很容易实现,但这强烈表明您有设计问题,您应该重新设计您的类和/或扩展名。像这样的碰撞强烈表明MyStruct 本身做了太多事情,因为它被MyProtocol1MyProtocol2 拉向多个方向。相反,这里应该有两个对象。 (组合而不是继承。)

    class MyStruct: MyProtocol1, MyProtocol2 {
        let service = PostService()
    
        func prot1Load<T: MyProtocol1>(t: T) {
            t.didLoad()
        }
    
        func prot2Load<T: MyProtocol2>(t: T) {
            t.didLoad()
        }
        init() {
            prot1Load(self)
            prot2Load(self)
        }
    }
    

    对于您在 cmets 中的特定示例,我将使用组合而不是继承。您将协议视为多重继承,这几乎是不正确的。而是由符合协议的东西组成。

    protocol LoadProviding {
        func load()
    }
    
    struct MyLoader1: LoadProviding {
        func load() {
            print("MyLoader1.didLoad()")
        }
    }
    
    struct MyLoader2: LoadProviding {
        func load() {
            print("MyLoader2.didLoad()")
        }
    }
    
    protocol Loader {
        var loaders: [LoadProviding] { get }
    }
    
    extension Loader {
        func loadAll() {
            for loader in loaders {
                loader.load()
            }
        }
    }
    
    class MyStruct: Loader {
        let service = PostService()
        let loaders: [LoadProviding] = [MyLoader1(), MyLoader2()]
    
        init() {
            loadAll()
        }
    }
    

    当然,您实际上不必让LoadProviding 成为一个完整的结构。如果这就是你所需要的,它可能只是一个函数:

    typealias LoadProviding = () -> Void
    
    func myLoader1() {
        print("MyLoader1.didLoad()")
    }
    
    func myLoader2() {
        print("MyLoader2.didLoad()")
    }
    
    protocol Loader {
        var loaders: [LoadProviding] { get }
    }
    
    extension Loader {
        func loadAll() {
            for loader in loaders {
                loader()
            }
        }
    }
    
    class MyStruct: Loader {
        let service = PostService()
        let loaders: [LoadProviding] = [myLoader1, myLoader2]
    
        init() {
            loadAll()
        }
    }
    

    如果您有时间浏览有关该主题的视频,您可能会对来自 dotSwift 的 Beyond Crusty: Real World Protocols 演讲感兴趣。这是关于这个和类似的问题。

    【讨论】:

    • 确实有味道。我正在尝试使用协议扩展来实现多重继承。例如,在我的UIViewControllerviewDidLoad 中,我想执行我的每个协议的每个didLoad 函数。
    • 那么你应该有一个像let loader: [Loader] 这样的属性,其中包含你需要的各种初始化。
    • 我可能会更深入地考虑这里的名称,以使它们符合当前的 Swift 风格准则,但以上仍然是我肯定会攻击它的方式。
    • 精彩的 dotSwift 演讲!谢谢!! Swift 中的泛型很弱 :(,希望它在 v3 中完善。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多