Я запускаю модульные тесты против контроллеров моего представления. В этом пакете есть два контроллера вида, один из которых имеет один проход к другому в файле раскадровки.UIStoryboardScene неожиданно отключает контроллер, не меняя счет сохранения
В тестах для обоих контроллеров я создаю свой тестовый объект таким же образом: я получаю storyboard
с storyboardWithName:factory:bundle:
(Typhoon для инъекций зависимостей использует заводскую вещь). Затем я использую storyboard.instantiateViewControllerWithIdentifier:
, чтобы получить VC.
В тестах для моего первого контроллера, все тесты проходят. Важно отметить, что метод контроллера deinit
контроллера вида называется ТОЛЬКО, когда сам Vc выходит из области видимости в тестовых случаях, или новый экземпляр становится объектом, а старый имеет показатель удержания, уменьшенный до 0.
Однако в тестах для второго VC тесты терпят неудачу в любом из этих случаев (VC выходит за рамки или новый становится предметом), потому что по какой-то причине происходит вызов [UIStoryboardScene dealloc]
, который, в свою очередь, вызывает на контроллере deinit
, даже хотя его счет сохранения должен оставаться равным 1 (сохраняется в тестовом примере). ARC отправляет другое сообщение о выпуске в VC, когда я устанавливаю новый экземпляр в качестве объекта, и получаю EXC_BAD_ACCESS (CODE=i386, GPFLT)
.
[UIStoryboardScene dealloc]
происходит в кучке скомпилированного кода в фреймворке XCTest (скопировано здесь), но я считаю, что это происходит, потому что тестовый пример заканчивается, хотя он не должен.
* thread #1: tid = 0x57cf91, 0x00000001089953a3 sbprod`Mode2ViewController.__deallocating_deinit(self=0x00007fd2bbdde5d0) + 19 at Mode2ViewController.swift:68, queue = 'com.apple.main-thread', stop reason = breakpoint 3.1
* frame #0: 0x00000001089953a3 sbprod`Mode2ViewController.__deallocating_deinit(self=0x00007fd2bbdde5d0) + 19 at Mode2ViewController.swift:68
frame #1: 0x0000000108995492 sbprod`@objc Mode2ViewController.__deallocating_deinit + 34 at Mode2ViewController.swift:0
frame #2: 0x000000010a1ff702 UIKit`-[UIStoryboardScene dealloc] + 36
frame #3: 0x000000010b04dafe libobjc.A.dylib`objc_object::sidetable_release(bool) + 232
frame #4: 0x000000010b04e0b8 libobjc.A.dylib`(anonymous namespace)::AutoreleasePoolPage::pop(void*) + 488
frame #5: 0x0000000115841f37 XCTest`__24-[XCTestCase invokeTest]_block_invoke_2 + 430
frame #6: 0x0000000115876613 XCTest`-[XCTestContext performInScope:] + 190
frame #7: 0x0000000115841d78 XCTest`-[XCTestCase invokeTest] + 169
frame #8: 0x00000001158423a2 XCTest`-[XCTestCase performTest:] + 459
frame #9: 0x000000011583fcf7 XCTest`-[XCTestSuite performTest:] + 396
frame #10: 0x000000011583fcf7 XCTest`-[XCTestSuite performTest:] + 396
frame #11: 0x000000011583fcf7 XCTest`-[XCTestSuite performTest:] + 396
frame #12: 0x000000011582cb10 XCTest`__25-[XCTestDriver _runSuite]_block_invoke + 51
frame #13: 0x000000011584db4c XCTest`-[XCTestObservationCenter _observeTestExecutionForBlock:] + 640
frame #14: 0x000000011582ca55 XCTest`-[XCTestDriver _runSuite] + 453
frame #15: 0x000000011582d7d1 XCTest`-[XCTestDriver _checkForTestManager] + 259
frame #16: 0x0000000115877a9a XCTest`_XCTestMain + 628
Итак:
Почему UIStoryboardScene создается на всех? Мне не нужна сцена, просто VC, для моих тестов. Похоже, что это не было в предыдущем случае. Я думаю, что только различия в том, что раскадровка заботится между двумя VC, состоит в том, что у одного из них есть segue, а другой - из него.
Если UIStoryboardScene на самом деле существует в обоих случаях, почему это
deinit
мой VC, прежде чем он должен был?Мои тестовые примеры также не очень отличаются друг от друга в сфере видимости, однако этот тестовый пример пропускает некоторые замыкания на места, и я не на 100% влияю на то, как замыкания влияют на ARC.
EDIT: Соответствующий код показан здесь.
В моем тестовом примере, который производится с помощью Quick:
override func spec() {
var subject: Mode2ViewController!
let presentDisplayString = "DesiredString"
describe("Mode2ViewController") {
describe("loading") {
describe("date and location") {
context("when location is available") {
beforeEach {
let system = MockSystemComponents.CreateWith(location: true, groups: nil)
let assembly = ApplicationAssembly().activateWithCollaboratingAssemblies([
system
])
//crash occurs on next line, before the 2nd test case, because the
//old subject has already been deallocated (by UIStoryboardScene)
//but reassigning this var, which had retains it, triggers a release
subject = assembly.mode2ViewController() as! Mode2ViewController
let _ = subject.view
}
it("records the location") {
expect(subject).notTo(beNil())
}
it("displays the location as a meaningful string") {
expect(subject.locationLabel.text).to(equal(presentDisplayString))
}
}
}
}
}
}
Примечание: в трассировки стека, приведенной выше (первого редактирования) от точки останова, я вставил в Mode2ViewController.deinit()
.
Вам нужно будет показать свой код. – jtbandes
@jtbandes Я добавил код, который вызывает 2-й (аварийный) выпуск. сообщите мне, если вам нужны другие фрагменты кода. – Quintana