Динамическое расширение протокола диспетчеризации не работает для нескольких целей - PullRequest
0 голосов
/ 26 ноября 2018

Это мой код в моей основной цели (поэтому не цели теста):

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(), я ожидаю, что она будет запущена.

1 Ответ

0 голосов
/ 26 ноября 2018

Ваш Project модуль декларирует соответствие MyClass ProtocolA.

Swift реализует это соответствие, используя структуру данных, называемую «таблица-свидетель протокола».Для каждого метода, объявленного протоколом, таблица свидетелей содержит функцию, которая вызывает фактическую реализацию метода для соответствующего типа.

Конкретнее, существует таблица свидетелей для соответствия MyClassProtocolA.Эта таблица свидетелей содержит функцию для метода 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 в вашем тестовом комплекте не может изменить содержимое таблицы свидетелей.Слишком поздно для этого.Таблица свидетелей была полностью определена, когда Swift сгенерировал модуль Project.

Вот почему это должно быть так:

Предположим, я автор модуля 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).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...