4

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

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(),我希望它会火。

4

1 回答 1

2

您的Project模块声明MyClass符合ProtocolA.

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

具体来说,有一个符合MyClassto的见证表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不能更改见证表的内容。为时已晚。ProjectSwift 生成模块时,见证表已完全定义。

这就是为什么必须这样:

假设我是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

于 2018-11-26T20:28:36.467 回答