使用具有通用数据类型的协议在屏幕之间传递数据

Using protocol with generic data type to pass data between screens

我是 Android 开始学习的开发者 iOS。我正在尝试在主从样式应用程序之间传递数据。 我得到了 controller1,其中包含 ToDo 项列表,controller2 允许创建新的 ToDo 项并将其添加到 controller1 的列表中。

我创建了一个协议:

protocol ListDataHolder {
    
    associatedtype T
    
    func addItem(item: T)
    
    func reloadData()
}

controller1prepare 中分配了 self

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let controller2 = segue.destination as? Controller2{
            controller2.toDoDataHolder = self
        }
    } 

controller2

中声明委托
// how do I tell to use ToDo class for generic type here
var toDoDataHolder: ListDataHolder? = nil

并像这样使用它:

@IBAction func onAddClicked(_ sender: Any) {
        let toDo = ToDo()
        ...
        toDoDataHolder?.addItem(item: toDo)
        toDoDataHolder?.reloadData()
        navigationController?.popViewController(animated: true)
    }

这样做时我遇到了一些错误:

对于委托声明:

Protocol 'ListDataHolder' can only be used as a generic constraint because it has Self or associated type requirements

使用addItem()时:

Cannot convert value of type 'ToDo' to expected argument type 'ListDataHolder.T'
Insert ' as! ListDataHolder.T'
Member 'addItem' cannot be used on value of protocol type 'ListDataHolder'; use a generic constraint instead

当我从协议中删除泛型而只有 addItem(item: ToDo) 时,一切正常。但我希望能够将 ListDataHolder 与任何数据类型一起使用。

这只是我的实验,我并不是在寻找在控制器之间传递数据的正确方法。

编辑:您可以在此 GitHub 存储库中找到完整代码:github.com/Sermilion/ios_learning

如果您总是将 ToDo 实例传递给 addItem 方法,请使用 ToDo 而不是在 ListDataHolder 协议中使用关联类型。

如果您需要此协议 addItem 方法将适用于任何通用类型,请使用以下代码。

protocol ListDataHolder {
    func addItem<T:Decodable>(item: T)
    func reloadData()
}

struct ToDo: Decodable {

}

class A: ListDataHolder {
   
    func addItem<T:Decodable>(item: T) {
          print("Add Item called")
    }
    
    func reloadData(){

    }
}

这里需要实现委托设计模式。 请详细查看委托设计模式。

您需要做的是使第二个视图控制器使用协议 来限制 class 使用(或持有)的对象类型符合ListDataHolder

这可以在视图控制器的声明中完成

class SecondViewController<Holder: ListDataHolder>: UIViewController where Holder.T == ToDo

那么您的方法 onAddClicked 将按原样工作。