您的Project
模块声明MyClass
符合ProtocolA
.
Swift 使用一种称为“协议见证表”的数据结构来实现这种一致性。对于协议声明的每个方法,见证表都包含一个函数,该函数调用符合类型的方法的实际实现。
具体来说,有一个符合MyClass
to的见证表ProtocolA
。该见证表包含由dontCrash
声明的方法的函数ProtocolA
。见证表中的该函数调用该MyClass
dontCrash
方法。
当您的测试用例命中时,您可以从堆栈跟踪中的协议见证表中看到该函数fatalError
:
#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
对协议见证表中函数的调用。第 9 帧是从协议见证表函数调用dontCrash
.
Swift 在声明一致性的模块中发出协议见证表。因此,在您的情况下,见证表是Project
模块的一部分。
您在测试包中的覆盖dontCrash
不能更改见证表的内容。为时已晚。Project
Swift 生成模块时,见证表已完全定义。
这就是为什么必须这样:
假设我是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