gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-11-30 11:40:54 25 4
gpt4 key购买 nike

为了在 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/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com