【问题标题】:How to pass a sub protocol type where a super protocol type is expected in Swift? (Think passing subclass type for baseclass type but for protocols)如何在 Swift 中传递需要超级协议类型的子协议类型? (考虑为基类类型传递子类类型,但为协议传递)
【发布时间】:2017-01-03 01:22:48
【问题描述】:

我在 Swift 中遇到了一个协议继承问题。我正在尝试构建一个接受协议类型 X 数组的 API。该 API 的使用者应该能够传入任何类型的协议类型 X 或任何从协议 X 继承的子协议类型 X'。看起来这可以- 如果没有某种形式的显式将协议类型 X' 转换回协议类型 X,就无法在 Swift 中实现。为什么会这样?我没有看到类类型的这种行为(我怀疑可能是因为继承的工作方式明显不同)。

这是我的代码:

public protocol InjectableService
{}

public protocol ClientDependency
{
    func serviceDependencies() -> [InjectableService.Type]
    func injectDependencies(dependencies: [InjectableService])
}



public protocol TouchIdServiceInterface : InjectableService
{
    func biometricAuthenticate() -> Void
}

public protocol PasswordServiceInterface : InjectableService
{
    func passwordAuthenticate() -> Void
}

public class LoginController : ClientDependency
{
    private var services: [InjectableService]!

    public func injectDependencies(dependencies: [InjectableService]) {
        services = dependencies
    }

    public func serviceDependencies() -> [InjectableService.Type] {
        return [TouchIdServiceInterface.self as! InjectableService.Type , PasswordServiceInterface.self as! InjectableService.Type]
    }


}

正如您在上面的函数 serviceDependencies() 中看到的那样,我必须将子协议类型转换回其超级协议类型。如果我删除演员表,我会收到一个编译器警告,明确要求我这样做。

有什么办法可以避免这种情况吗?我喜欢它可以为你解决的类类型。我真的很想避免这个转换问题,因为它会使使用 API 变得非常笨拙。谢谢!

【问题讨论】:

  • 有趣。玩了几分钟,也搞不明白。
  • 您将通过更简单的测试用例获得更好的答案:swiftlang.ng.bluemix.net/#/repl/586b038cfbf6b56680985c96
  • TouchIdServiceInterface.self 返回 TouchIdServiceInterface.Protocol 的类型,而不是 TouchIdServiceInterface.Type。
  • func serviceDependencies() -> [InjectableService.Type]的返回类型有什么用?
  • 这是Protocol doesn't conform to itself? - TouchIdServiceInterface.self 的(又一个)变体(协议类型本身 - 类型为TouchIdServiceInterface.Protocol,而不是TouchIdServiceInterface.Type)不能转换为InjectableService.Type符合InjectableService的类型——TouchIdServiceInterface不符合)。

标签: swift inheritance swift-protocols protocol-inheritance


【解决方案1】:

来自苹果文档:

元类型类型

元类型类型是指任何类型的类型,包括类类型, 结构类型、枚举类型和协议类型。

类、结构或枚举类型的元类型是 该类型后跟.Type。协议类型的元类型——不是 在运行时符合协议的具体类型——是 该协议后跟.Protocol。例如,元类型 类类型 SomeClass 是 SomeClass.Type 和元类型 协议 SomeProtocol 是SomeProtocol.Protocol

您可以使用后缀self 表达式将类型作为值访问。 例如,SomeClass.self 返回 SomeClass 本身,而不是实例 SomeClass的。 SomeProtocol.self 返回 SomeProtocol 本身,而不是 在运行时符合 SomeProtocol 的类型的实例。

如果要返回[InjectableService.Type],需要定义各个协议的类。

public protocol InjectableService
{}

public protocol ClientDependency
{
    func injectDependencies(dependencies: [InjectableService])
    func serviceDependencies() -> [InjectableService.Type]
}

public protocol TouchIdServiceInterface : InjectableService
{
    func biometricAuthenticate() -> Void
}

public protocol PasswordServiceInterface : InjectableService
{
    func passwordAuthenticate() -> Void
}

public class TouchIdService: TouchIdServiceInterface {
    public func biometricAuthenticate() -> Void { }
}

public class PasswordService: PasswordServiceInterface {
    public func passwordAuthenticate() -> Void { }
}


public class LoginController: ClientDependency
{
    private var services: [InjectableService]!

    public func injectDependencies(dependencies: [InjectableService]) {
        services = dependencies
    }

    public func serviceDependencies() -> [InjectableService.Type] {
        let ts: [InjectableService.Type] = [
            TouchIdService.self,
            PasswordService.self
        ]
        return ts
    }

}

let controller = LoginController()
let services = controller.serviceDependencies()

更新

public protocol ClientDependency
{
    func injectDependencies(dependencies: [InjectableService])
    func serviceDependencies() -> [Any]
}

登录控制器:

public func serviceDependencies() -> [Any] {
    let ts: [Any] = [
        TouchIdServiceInterface.self,
        TouchIdServiceInterface.self
    ]
    return ts
}

测试:

let controller = LoginController()
let services = controller.serviceDependencies()
if let touchIdService = services.first as? TouchIdServiceInterface.Protocol {
    print(touchIdService)
}

【讨论】:

  • 这样做会破坏我试图设计的目的。作为 ClientDependency 的 LoginController 不应仅返回协议的实际类类型/对象。我正在将这两件事解耦。
  • InjectableService.Type 必须是类类型,即使你试图从TouchIdServiceInterface.self as! InjectableService.Type 强制转换它,它也不会编译。
  • 对不起,是的,它已编译但是当您调用该方法时,您将遇到运行时错误。如果您想使用协议而不是类,请查看我的更新答案。
  • 是的,我想我必须像你在更新中提到的那样做一些事情,这很糟糕。这就是我想念 objC 运行时检查方法的地方
  • 我还是不知道你对serviceDependencies()的返回值做了什么或者它的目的是什么?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-07-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多