【问题标题】:Swift protocol inside of protocol协议内的 Swift 协议
【发布时间】:2019-11-11 12:11:17
【问题描述】:

我遇到了 Swift 协议的问题。我有这样的事情:

protocol OnboardingPage {
  var viewModel: OnboardingPageViewModel! { get }
}

protocol OnboardingPageViewModel {
  func getValue() -> Bool
}

// ---

class FirstNameViewController: UIViewController, OnboardingPage {
  var viewModel: FirstNameViewModel!
}

class FirstNameViewModel: OnboardingPageViewModel {
  func getValue() -> Bool {
    return true
  }
}

class GenderViewController: UIViewController, OnboardingPage {
  var viewModel: GenderViewModel!
}

class GenderViewModel: OnboardingPageViewModel {
  func getValue() -> Bool {
    return false
  }
}

我有一堆符合OnboardingPage 协议的视图控制器,它们都有一个完全符合OnboardingPageViewModel 协议的视图模型。

但遗憾的是,上面的代码不起作用:它说FirstNameViewController 不确认OnboardingPage 协议,因为它的viewModel 属性类型为FirstNameViewModel 而不是OnboardingPageViewModel - 即使@987654329 @是OnboardingPageViewModel

我该如何解决这个问题?

【问题讨论】:

  • Type that conforms to protocolprotocol 不一样。 FirstNameViewModel 不是 OnboardingPage。还有T!T不一样。
  • 你对 T 的看法是绝对正确的!我修正了我的例子(在我的真实代码中是不同的)。
  • FWIW 你的getValue 功能是模糊的。获得什么价值?
  • 只是一个展示协议和 2 个实现的例子。我认为的真正问题无关紧要?

标签: swift swift-protocols


【解决方案1】:

使用关联类型 OnboardingPageViewModel 类型 ViewModel

已编辑

protocol OnboardingPage {
    associatedtype ViewModel: OnboardingPageViewModel

    var viewModel: ViewModel { get }
}

protocol OnboardingPageViewModel {
    func getValue() -> Bool
}

class FirstNameViewController: OnboardingPage {
    let viewModel: FirstNameViewModel

    init(viewModel: FirstNameViewModel) {
        self.viewModel = viewModel
    }
}

class FirstNameViewModel: OnboardingPageViewModel {
    func getValue() -> Bool {
        return true
    }
}

class GenderViewController: OnboardingPage {
    let viewModel: GenderViewModel

    init(viewModel: GenderViewModel) {
        self.viewModel = viewModel
    }
}

class GenderViewModel: OnboardingPageViewModel {
    func getValue() -> Bool {
        return false
    }
}

【讨论】:

  • 如果这只是associatedtype 的一个条件,您甚至可以跳过where 并将其声明为associatedtype ViewModel: OnboardingPageViewModel
  • 谢谢!但是,现在我遇到了一个新问题,当我尝试在我的容器视图控制器中执行类似的操作时收到错误 Protocol 'OnboardingPage' can only be used as a generic constraint because it has Self or associated type requirementslet pages: [OnboardingPage] = [FirstNameViewController(), GenderViewController()]
  • where 的用法仍然适用吗?如果是这样,您可以在代码中的某个位置包含它。否则会令人困惑,因为像我这样的读者正在寻找您的用法,但最终一无所获:|
【解决方案2】:

您可以通过删除关联类型来解决您的页面数组issue

protocol OnboardingPage {    
    var viewModel: OnboardingPageViewModel { get }
}

protocol OnboardingPageViewModel {
    func getValue() -> Bool
}

class FirstNameViewModel: OnboardingPageViewModel {
    func getValue() -> Bool {
        return true
    }
}

class FirstNameViewController: OnboardingPage {
    let viewModel: OnboardingPageViewModel

    init(viewModel: FirstNameViewModel) {
        self.viewModel = viewModel
    }
}

class GenderViewModel: OnboardingPageViewModel {
    func getValue() -> Bool {
        return false
    }
}

class GenderViewController: OnboardingPage {
    let viewModel: OnboardingPageViewModel

    init(viewModel: GenderViewModel) {
        self.viewModel = viewModel
    }
}

用法:

let nameModel = FirstNameViewModel()
let genderModel = GenderViewModel()

let array: [OnboardingPage] = [FirstNameViewController(viewModel: nameModel), GenderViewController(viewModel: genderModel)]

for page in array {
    print(page.viewModel.getValue())
}

但是,以这种方式,您的模型的具体类型将不适用于符合 OnboardingPage 的类的调用者。您可以尝试通过在OnboardingPage 中实现访问者模式或增强其功能来解决它​​。

当然,您始终可以使用 Cruz 示例编写 let pages: [Any] = [FirstNameViewController(), GenderViewController()],但它很难看,如果可能,我强烈建议您不要在 API 中使用 Any

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多