gpt4 book ai didi

swift - 动态调度协议(protocol)扩展不适用于多个目标

转载 作者:可可西里 更新时间:2023-11-01 01:56:05 26 4
gpt4 key购买 nike

这是我在主要目标中的代码(所以不是测试目标):

protocol ProtocolA {
func dontCrash()
}

extension ProtocolA {
func dontCrash() {
fatalError()
}

func tryCrash() {
dontCrash()
}
}

class MyClass: ProtocolA {}

在我的测试目标(如此不同的目标)中,我得到了这段代码:

import XCTest
@testable import Project

extension MyClass {
func dontCrash() {
print("I dont crash")
}
}

class ProjectTests: XCTestCase {
func testExample() {
MyClass().tryCrash()
}
}

它崩溃了。为什么不使用动态调度机制呢? MyClass 有它自己的 dontCrash() 实现,我希望它能触发。

最佳答案

您的 Project 模块声明 MyClass 符合 ProtocolA

Swift 使用称为“协议(protocol)见证表”的数据结构来实现这种一致性。对于协议(protocol)声明的每个方法,见证表包含一个函数,该函数调用符合类型的方法的实际实现。

具体来说,MyClassProtocolA的一致性有一个witness table。该见证表包含 ProtocolA 声明的 dontCrash 方法的函数。 witness 表中的该函数调用 MyClass dontCrash 方法。

当您的测试用例遇到 fatalError 时,您可以在堆栈跟踪中看到协议(protocol)见证表中的函数:

#8  0x00000001003ab9d9 in _assertionFailure(_:_:file:line:flags:) ()
#9 0x00000001000016fc in ProtocolA.dontCrash() at /Users/rmayoff/TestProjects/Project/Project/AppDelegate.swift:11
#10 0x0000000100001868 in protocol witness for ProtocolA.dontCrash() in conformance MyClass ()
#11 0x000000010000171e in ProtocolA.tryCrash() at /Users/rmayoff/TestProjects/Project/Project/AppDelegate.swift:15
#12 0x00000001030f1987 in ProjectTests.testExample() at /Users/rmayoff/TestProjects/Project/ProjectTests/ProjectTests.swift:12
#13 0x00000001030f19c4 in @objc ProjectTests.testExample() ()

第 10 帧是 tryCrash 对协议(protocol)见证表中函数的调用。帧 #9 是从协议(protocol)见证表函数到 dontCrash 实际实现的调用。

Swift 在声明一致性的模块中发出协议(protocol)见证表。因此,在您的情况下,见证表是 Project 模块的一部分。

您在测试包中覆盖 dontCrash 不能更改见证表的内容。为时已晚。当 Swift 生成 Project 模块时,witness 表被完全定义。

这就是为什么它必须是这样的:

假设我是 Project 模块的作者,而您只是它的用户。当我编写 Project 模块时,我知道调用 MyClass().dontCrash() 会调用 fatalError,我依赖于此行为。在 Project 的许多地方,我特别调用了 MyClass().dontCrash(),因为我知道它会调用 fatalError。作为 Project 的用户,您不知道 Project 在多大程度上依赖于该行为。

现在您在您的应用中使用了 Project 模块,但您追溯更改了 MyClass().dontCrash() 以不调用 fatalError。现在所有那些 Project 调用 MyClass().dontCrash() 的地方都没有按照我编写 Project 时预期的方式运行模块。您已经破坏了 Project 模块,即使您没有更改 Project 模块或 Project 导入的任何模块的源代码.

这对 Project 模块的正确操作至关重要。因此,更改 MyClass().dontCrash() 含义的唯一方法(当从 Project 模块内部调用时)是更改 Project 的源代码 模块本身(或更改 Project 导入的东西的源代码)。

关于swift - 动态调度协议(protocol)扩展不适用于多个目标,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53487613/

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