0

Я запускаю модульные тесты против контроллеров моего представления. В этом пакете есть два контроллера вида, один из которых имеет один проход к другому в файле раскадровки.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 

Итак:

  1. Почему UIStoryboardScene создается на всех? Мне не нужна сцена, просто VC, для моих тестов. Похоже, что это не было в предыдущем случае. Я думаю, что только различия в том, что раскадровка заботится между двумя VC, состоит в том, что у одного из них есть segue, а другой - из него.

  2. Если UIStoryboardScene на самом деле существует в обоих случаях, почему это deinit мой VC, прежде чем он должен был?

  3. Мои тестовые примеры также не очень отличаются друг от друга в сфере видимости, однако этот тестовый пример пропускает некоторые замыкания на места, и я не на 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().

+0

Вам нужно будет показать свой код. – jtbandes

+0

@jtbandes Я добавил код, который вызывает 2-й (аварийный) выпуск. сообщите мне, если вам нужны другие фрагменты кода. – Quintana

ответ

0

Оказывается, проблема заключалась в том, что цель c рассматривает вещи, которые начинаются с ключевого слова new по-разному относительно их количества удержания. По этой причине тайфун не может обрабатывать методы, начинающиеся с new (см. issue).

Решение: переименуйте мой метод тайфуна и генерируемый объект имеет правильный счетчик удержания.