【问题标题】:Implement delegate using generic protocol in Swift在 Swift 中使用泛型协议实现委托
【发布时间】:2014-09-09 14:12:42
【问题描述】:

我正在尝试创建一个委托协议,该协议实现了一个传递泛型类型数组的函数。我尝试了几种组合,但似乎没有一种能奏效。

这是我所达到的最近似的东西。这是协议:

     protocol APIControllerProtocol {

          typealias T

          func didReceiveAPIResults(results: [T])
     }

这是委托对象:

    class APIController<U:APIControllerProtocol> {

         typealias ElementType = U
         var delegate: ElementType?

         init(delegate: ElementType){

             self.delegate = delegate

         }

         func getAPIResults(){

              // Perform some action before delegation
              // "results" is an Array of dictionaries got from NSJSONSerialization

              self.delegate?.didReceiveAPIResults(results.map{dict in Album(json:dict)})

         }

     }

但是,最后一行出现此错误:“Album is not convertible to U.T”

“专辑”是用于返回结果的模型对象。

我做错了什么?

编辑:

Mike S advice 之后,我将协议方法didReceiveAPIResults 设为通用函数,并指定T 在委托中的内容。但是,当接收类型 T 的参数并将其分配给委托中的属性时,我收到错误:“T 与 T 不同”

class TestDelegate: APIControllerProtocol {
    typealias T = Album
    var albums:[T] = [T]()

    func didReceiveAPIResults<T>(results: [T]) {
        // ...

        self.albums = results //ERROR: "T is not identical to T"
    }
}

【问题讨论】:

    标签: ios swift


    【解决方案1】:

    APIControllerProtocol 中的 didReceiveAPIResults 声明必须是泛型函数,以便泛型类型 T 正确传递给它。

    protocol APIControllerProtocol {
        typealias T
    
        func didReceiveAPIResults<T>(results: [T])
    }
    

    注意:这意味着您的委托定义需要定义 T 是什么:

    class TestDelegate: APIControllerProtocol {
        typealias T = Album
    
        func didReceiveAPIResults<T>(results: [T]) {
            // ...
        }
    }
    

    更新:虽然上面的代码确实消除了最初的错误,但事实证明它更像是一种解决方法,并没有真正解决问题的根源。

    真正的问题似乎是编译器无法毫不含糊地协调 U.T 的含义。这实际上很容易解决,我们只需要给它一个更精确的定义(注意APIController 定义中的where 子句)

    protocol APIControllerProtocol {
        typealias T
        func didReceiveAPIResults(results: [T])
    }
    
    class APIController<U:APIControllerProtocol where U.T == Album> {
        typealias ElementType = U
        // ...
    }
    

    注意:我删除了之前在协议中添加到函数中的&lt;T&gt;;这不再需要,以后会导致问题。

    这样,TestDelegate 类可以按预期工作(您甚至不再需要 typealias):

    class TestDelegate: APIControllerProtocol {
        var albums: [Album]? = nil
    
        func didReceiveAPIResults(results: [Album]) {
            albums = results
        }
    }
    

    【讨论】:

    • 谢谢!我已经根据您的建议编辑了问题,但是在尝试将方法参数分配给委托中的属性时出现错误:“T 与 T 不同”(有关更多详细信息,请参阅编辑后的问题)。也许这是一个非常基本的泛型问题,但我真的迷路了......
    • 抱歉,看来是我一开始误导了你。我用更好的解决方案更新了答案。
    • 非常感谢,非常感谢您的帮助。我已将您的答案标记为正确答案(我是新来的,所以请告诉我是否还有其他需要做的事情)。不过,我还有一个问题,您认为是否可以让委托人 (APIController) 与“专辑”类型无关?我的意思是我想使用相同的 APIController 来执行具有不同类型对象的任务,并将结果的处理委托给将知道如何管理模型类型(专辑、曲目或其他)的委托对象。无论如何,我不是 100% 确定这是否有意义
    • 最后,通过使用 AnyObject 而不是 Album,我想我已经解决了我提出的最后一个问题。我想我可以使用所有模型对象共有的一些基本对象。非常感谢您的帮助!
    • 很高兴我能帮上忙!我建议为您的专辑、曲目等对象使用通用基类。主要是因为你的定义越具体,Swift 的类型安全就越能帮助你,你会遇到的问题就越少。
    猜你喜欢
    • 2023-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多