swift - 使用更通用类型的系统类的协议(protocol)一致性

标签 swift

为了在 Swift 中模拟对象进行测试,我通常遵循这样的模式:编写一个协议(protocol)来描述我想要的对象的行为,然后使用 Cuckoo 为其生成模拟以进行测试。

通常,这些协议(protocol)直接映射到现有类型,这工作得很好,直到我需要使现有类型与我的新协议(protocol)类型一起使用。

public typealias RequestCompletionHandler = (Request, Error?) -> Swift.Void

public protocol Request {
  var results: [Any]? { get }
  var completionHandler: RequestCompletionHandler? { get }
}

extension VNRequest: Request {}

这里,VNRequest 已经有一个名为 completionHandler 的成员,它返回以下类型:

公共(public)类型别名 VNRequestCompletionHandler = (VNRequest, Error?) -> Swift.Void

从技术上讲,所有这些类型都应该匹配,但显然这对于​​类型求解器来说并不是一个很容易解决的场景,因此编译器对此不太满意。

起初我以为我可以通过执行以下操作来引用原始的completionBlock实现:

extension VNRequest: Request {
  public var completionHandler: RequestCompletionHandler? {
    return (self as VNRequest).completionHandler
  }
}

但它对此也不太高兴。

关于如何最好地做到这一点有什么建议吗?我考虑过在协议(protocol)中使用不同的名称(例如:completionBlock_completionBlock$),这可行,但有点杂乱。

最佳答案

出现该问题的原因是 Swift 在闭包返回类型方面是协变的,而在其参数方面是逆变的。这意味着 (VNRequest, Error?) -> Void 不能用在需要 (Request, Error?) -> Void 的地方(反之亦然) )。

您可以通过使用 Request 协议(protocol)中的关联类型来解决您的问题:

public protocol Request {
    associatedtype RequestType = Self
    var results: [Any]? { get }
    var completionHandler: ((RequestType, Error?) -> Void)? { get }
}

上述协议(protocol)定义将使 VNRequest 类进行编译,因为编译器将检测 RequestType 的匹配。

不过,缺点是具有关联类型的协议(protocol)在使用位置方面存在一些限制,并且将它们作为函数参数传递将需要一些 where 子句才能使它们起作用。

<小时/>

另一种选择是使用 Self 作为完成处理程序的参数:

public typealias RequestCompletionHandler<T> = (T, Error?) -> Swift.Void

public protocol Request {
    var results: [Any]? { get }
    var completionHandler: RequestCompletionHandler<Self>? { get }
}

这也将解决一致性问题,但也有一些限制:VNRequest 必须是最终的,使用 Request 的函数必须是通用的:

func send<R: Request>(_ request: R)

关于swift - 使用更通用类型的系统类的协议(protocol)一致性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49298212/

相关文章:

swift - 如何在 MapKit、Swift 5 中搜索城市?

ios - 在 UIView 外绘制阴影

swift - 类层次结构 - 类只能由另一个类调用

ios - 从 2 个不同的数组加载 tableview

objective-c - 在 iOS 8.0 项目中安装 ReactiveCocoa 3.0.0-alpha.1 的问题

ios - 使用 Swift 理解坐标系 SpriteKit 的问题

swift - .dynamicType 已弃用。使用 'type(of ...)' 代替

swift - Apple 关于在 if 语句中强制展开的 Swift 文档中的错误?

ios - 闭包执行

swift - NSData baseEncodedString 返回 nil