Сбой CoreMotion (только iPad) при вызове stopDeviceMotionUpdates

У нас есть экземпляр CMMotionManager в нашем приложении, которое мы используем для получения обновлений сенсора с частотой 5 Гц. Ниже приведен код, который мы используем для запуска обновлений движения:

[self.motionManager
startDeviceMotionUpdatesUsingReferenceFrame:
CMAttitudeReferenceFrameXMagneticNorthZVertical
toQueue:operationQueue
withHandler:^(CMDeviceMotion *motion, NSError *error) {
if (!error) { 
   [self doSomethingWithMotion:motion];
} else { ... }

Вышеуказанный метод всегда вызывается в основном потоке.

Теперь, как только мы закончим с нашей задачей, мы прекращаем обновление движений, вызывая следующий метод снова в основном потоке:

- (void)stopMotionUpdates {
    // This might get called on concurrent threads.
    // So better using sync block + make it idempotent
    @synchronized(self) {
        if (_motionManager.deviceMotionActive) {
            [_motionManager stopDeviceMotionUpdates];
            _prevPoint = nil;
        }
    }
}

Проблема, с которой мы сталкиваемся, заключается в том, что stopMotionUpdates сбой, что тоже, только в iPads. Мы протестировали это на iPhone и iPad с разными версиями ОС, и мы получили крах только на iPads (мини 1,2 и сетчатке/без сетчатки) для iOS7 и iOS8. Кроме того, мы не можем воспроизвести авария на всех iPads, которые мы используем для тестирования, но только несколько. Ниже приведены журналы сбоев для основной и разбитой нити:

Thread : com.apple.main-thread
0  libsystem_kernel.dylib         0x00000001935f1cdc semaphore_wait_trap + 8
1  libdispatch.dylib              0x00000001934fbb3c _dispatch_semaphore_wait_slow + 252
2  CoreMotion                     0x0000000186bf67d4 (null)
3  CoreMotion                     0x0000000186be3698 (null)
4  MyApp                          0x00000001002f7434 -[MyAppMotionManager stopMotionUpdates]
...
...
12 MyApp                          0x00000001002e94f8 __getDispatchTimer_block_invoke
13 libdispatch.dylib              0x00000001934f3fd4 _dispatch_client_callout + 16
14 libdispatch.dylib              0x00000001934f5b90 _dispatch_source_invoke + 500
15 libdispatch.dylib              0x00000001934f7180 _dispatch_main_queue_callback_4CF + 244
16 CoreFoundation                 0x00000001864fec2c __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 12
17 CoreFoundation                 0x00000001864fcf6c __CFRunLoopRun + 1452
18 CoreFoundation                 0x000000018643dc20 CFRunLoopRunSpecific + 452
19 GraphicsServices               0x000000018c0ddc0c GSEventRunModal + 168
20 UIKit                          0x000000018956efdc UIApplicationMain + 1156
21 MyApp                          0x00000001000c9850 main (main.m:14)
22 libdyld.dylib                  0x000000019350faa0 start + 4

EXC_BREAKPOINT UNKNOWN at 0x0000000186c257ac
Thread : Crashed: Thread
0  CoreMotion                     0x0000000186c257ac (null) + 110504
1  CoreMotion                     0x0000000186c25774 (null) + 110448
2  CoreMotion                     0x0000000186bf3c84 (null)
3  CoreMotion                     0x0000000186bf67ec (null)
4  CoreMotion                     0x0000000186bf3b80 (null)
5  CoreMotion                     0x0000000186c24c48 (null) + 107588
6  CoreMotion                     0x0000000186bf67ec (null)
7  CoreMotion                     0x0000000186c24ba4 (null) + 107424
8  CoreMotion                     0x0000000186be3b9c (null)
9  CoreMotion                     0x0000000186bf6860 (null)
10 CoreFoundation                 0x00000001864ff680 __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 20
11 CoreFoundation                 0x00000001864fe838 __CFRunLoopDoBlocks + 300
12 CoreFoundation                 0x00000001864fd0a4 __CFRunLoopRun + 1764
13 CoreFoundation                 0x000000018643dc20 CFRunLoopRunSpecific + 452
14 CoreFoundation                 0x00000001864932a8 CFRunLoopRun + 112
15 CoreMotion                     0x0000000186bf653c (null)
16 libsystem_pthread.dylib        0x000000019368be1c _pthread_body + 168
17 libsystem_pthread.dylib        0x000000019368bd74 _pthread_body

Поскольку мы ссылаемся на self в блоке обработчика обновления движения, объект не должен быть освобожден до полной очистки всей очереди. Любая помощь приветствуется:)

Ответы

Ответ 1

Возможно, вы перегрузили основной поток.

  • Как правило, вы никогда не должны ничего делать в основном потоке, который занимает больше или примерно секунду.

  • Основной поток отвечает за запуск пользовательского интерфейса. Если вы блокируете основной поток за какое-то значительное количество времени, пользовательский интерфейс становится неприемлемо невосприимчивым.

  • сторожевой таймер. Чтобы поддерживать отзывчивость пользовательского интерфейса, iOS включает механизм сторожевого таймера. Если ваше приложение не отвечает на определенные события пользовательского интерфейса (запускать, приостанавливать, возобновлять, завершать) во времени, или какая-то операция занимает немного больше времени в основном потоке, тогда сторожевой таймер убьет ваше приложение. Количество времени, которое сторожевой таймер дает вам, официально не документировано, но оно всегда меньше.

  • Любая такая операция должна выполняться в фоновом потоке, и вы можете легко сделать это, используя

    • dispatch_async

    • NSOperationQueue

    • performSelectorInBackground

Надеюсь, что это поможет.

Ответ 2

Ваша авария происходит из директивы @synchronized. Как вы видите, основной поток выполняет ваш код в @synchronized. Это блокирует контроллер вида, от доступа к любому другому потоку.

Поврежденный поток предназначен для CoreMotion, и я полагаю, что он пытался обновить движок устройства.

Я считаю, что крах случается только в некоторых случаях, когда CoreMotion пытается попытаться обновить в то же время, когда ваш основной поток проходит через синхронизированный.

Ответ 3

Где вы определяете "operationQueue", нет я, поэтому я предполагаю, что это не переменная уровня класса. Вы хотите, чтобы это было свойство класса с сильной ссылкой, чтобы оно не освобождалось при получении обновлений. Я также видел, что это происходит с переменными с расширенными функциями для dispatch_queue_t на вызовах Bluetooth.

Управление памятью объясняло бы неустойчивое поведение, но вы всегда могли бы сделать iOS более агрессивным, открыв другие приложения и кучу вкладок веб-сайта на iPad, о которых вы уже видели эту проблему.

Обновления CMMotion должны работать в порядке FIFO, чтобы быть точными, чтобы можно было объяснить, почему вы видите semafore_wait_trap в журналах.