【问题标题】:Swift Generic typesSwift 泛型类型
【发布时间】:2018-12-16 04:32:45
【问题描述】:

您好,我是 swift 和编写 iOS 应用程序的新手。 我想知道如何更好地使用泛型来传递我的结果数据。

我有一个代表从服务器提供人员数据

protocol PersonDataProvider {
    func dataReceived(_ result: PersonResult)
}

结果通过枚举可以有成功和失败状态。

enum PersonResult {
    case success
    case networkFailed
}

下面的类调用服务器api并获取数据,传回

class MyNetworkClass {

    var personDataProvider: PersonDataProvider
    func getDataFromServer() {
        personDataProvider.dataReceived(.success)
    }
}

下面是我订阅提供者的视图控制器

class MyViewController: PersonDataProvider {

    func dataReceived(_ result: PersonResult) {

        switch result {
        case .success:
            print("success")

        case .networkFailed:
            print("no network")

        }
    }

}

现在,我想发送带有成功块的额外信息,可以像下面的数据模型一样。它可以有任何类型。

class Employer:Person {
    let id
    let salary
}

class Student:Person {
    let id
    let rollNumber
}

我怎样才能做到这一点?我可以在协议中定义关联类型并实现如果是的话如何?

我如何订阅“PersonDataProvider”的“MyViewController”才能定义他期望从“成功”块获得的结果类型?

【问题讨论】:

  • 您认为为什么会涉及到 generics?您在代码中的什么位置“发送”“成功块”?
  • "getDataFromServer" 调用委托方法。那是我想通过成功块根据要求将“雇主”或“学生”数据作为参数传递的地方。我不确定如何实现它。我还希望“MyNetworkClass”在订阅时通过“PersonDataProvider”定义他想要的类型数据
  • 好的,Employer 和 Student 可以有一个共同的超类吗?因此,成功可能有一个关联的值,可能是其中之一。
  • @matt 是的,它可以有。在这种情况下如何使用关联值?

标签: ios swift generics swift4


【解决方案1】:

使用关联值作为 .success 案例的一部分。

假设您有一个超类 Person 用于您的类型的人。然后像这样定义你的枚举:

enum PersonResult {
    case success(Person)
    case networkFailed
}

现在,当您交回 .success 值时,您必须提供一个 Person 来使用它:

func getDataFromServer() {
    let person = Student.init(...) // initialize, or fetch, or whatever
    personDataProvider.dataReceived(.success(person))
}

这是一个完整的假例子,它模拟了整个事情;您必须更改细节,但重点是,它可以编译:

enum PersonResult {
    case success(Person)
    case networkFailed
}
protocol PersonDataProvider {
    func dataReceived(_ result: PersonResult)
}
class MyNetworkClass {
    var personDataProvider: PersonDataProvider!
    func getDataFromServer() {
        let person = Student.init(id: 3, rollNumber: 10)
        personDataProvider.dataReceived(.success(person))
    }
}
class Person {}
class Employer:Person {
    let id : Int
    let salary : Int
    init(id:Int, salary:Int) {
        self.id = id; self.salary = salary
    }
}
class Student:Person {
    let id : Int
    let rollNumber : Int
    init(id:Int, rollNumber:Int) {
        self.id = id; self.rollNumber = rollNumber
    }
}

【讨论】:

  • 感谢以上建议。我还不清楚相关的返回类型。 “PersonDataProvider”的订阅者有什么方法,即“MyViewController”可以定义它期望从成功块中获得什么类型的数据。
【解决方案2】:

您必须摆脱委托模式才能实现您想要的东西。相反,您将需要 Result 类型的通用 enum 和您的数据模型类型通用的网络 class

enum Result<T> {
    case success(T)
    case networkFailed
}

class MyNetworkClass<T: Person> {
    func getDataFromServer(completion: @escaping (Result<T>)-> Void) {
        // here goes your implementation (data fetching)
        // for demonstration purpose I just initialized object
        if let student = Student(id: 112233, rollNumber: 25) as? T {
            completion(Result.success(student))
        } else if let employer = Employer(id: 445566, salary: 5200) as? T {
            completion(Result.success(employer))
        } else {
            completion(Result.networkFailed)
        }
    }
}

那么你的用法是:

MyNetworkClass().getDataFromServer { (result: Result<Student>) in // you can also use Employer type
    switch result {
    case .success(let student):
        print(student.id)
    case .networkFailed:
        print("Error")
    }
}

【讨论】:

  • 感谢@nayem 的建议。我如何在这里使用关联类型,以便“MyViewController”可以判断他期望“PersonDataProvider”提供哪种类型的数据??
  • 您指的是Protocol with Associated Type (PAT)吗?我认为这过于复杂,不适合要求。虽然我没有像 delegate 那样使用 PAT,但您可能想看看这些:hereherehere
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-27
  • 1970-01-01
  • 2020-09-18
  • 1970-01-01
相关资源
最近更新 更多