核心运动(仅限iPad)在调用stopDeviceMotionUpdates时崩溃

15

我们的应用程序中有一个 CMMotionManager 实例,我们使用它以 5Hz 的频率获取传感器更新。以下是我们用来启动运动更新的代码:

[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崩溃,而且仅在iPad上出现。我们已经在不同OS版本的iPhone和iPad上进行了广泛测试,并且仅在iPad(mini 1,2和retina/non-retina)上发生崩溃,无论是iOS7还是iOS8。另外,我们不能在所有用于测试的iPad上复制崩溃,只有几个。以下是主线程和崩溃线程的崩溃日志:

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,因此对象在整个队列被刷新之前不应该被释放。 非常感谢任何帮助:)

3个回答

4

您可能正在过载主线程。

  • 一般而言,您不应该在主线程上执行耗时超过一秒钟的任何操作。

  • 主线程负责运行用户界面。如果您阻塞主线程相当长的时间,用户界面将变得无法接受地无响应。

  • 看门狗 - 为了保持用户界面的响应性,iOS包含了一个看门狗机制。如果您的应用程序未能及时响应某些用户界面事件(启动、挂起、恢复、终止)或某些操作在主线程上花费了较长时间,则看门狗将终止您的应用程���。看门狗给您的时间并没有正式记录,但它总是很短。

  • 任何此类操作都必须在后台线程上执行,您可以轻松使用以下方法:

    • dispatch_async

    • NSOperationQueue

    • performSelectorInBackground

希望这可以帮助到您。


这里有人在使用蓝牙时遇到了semaphore_wait_trap问题,并通过使用dispatch_async解决了该问题。http://goo.gl/PumFrT - Durai Amuthan.H
你确定"semaphore_wait_trap"是由于watchdoc超时引起的吗?我只是在UI线程中调用了stopMotionUpdates,并且主线程中没有进行任何繁重的操作。 - Yogesh Maheshwari
我不是很确定,但可能会导致这种情况。不要在主线程中调用任何与UI无关的内容。stopDeviceMotionUpdates 完成的平均或最长时间是多少?你能把代码移到其他线程并尝试吗?你可以尝试使用 dispatch_async 吗? - Durai Amuthan.H
1
根据此文献,以下情况也可能导致问题:1)过度释放/在使用后释放调度信号量对象 2)堆内存损坏 3)进程中的mach端口管理不当。 (参考:http://goo.gl/dRuaTW) - Durai Amuthan.H
1
由于无法重现该问题,您唯一能做的就是在 Open Radar (http://goo.gl/qjMLOj)或 Bug Reporter (http://goo.gl/TupH4m)中报告它,并在那里跟踪它。可能苹果技术人员会回复您。同时,如果我在这方面遇到任何有用的信息,我将在此线程中通知您。 - Durai Amuthan.H
显示剩余5条评论

2
你的崩溃来自于@synchronized指令。正如你所看到的,主线程正在执行@synchronized中的代码。这会锁定视图控制器,使其无法被其他线程访问。
崩溃的线程是用于CoreMotion的,我猜它试图在设备运动方面提供更新。
我认为崩溃只会在某些情况下发生,即当CoreMotion恰好尝试在主线程通过同步运行时进行更新。

stopMotionUpdates方法不在viewController中,我有一个不同的MotionManager来处理和传递运动数据给代理。当运动数据需求完成时,我们调用stopMotionUpdates。在这种情况下,@synchronized没有创建任何互斥锁。 - Yogesh Maheshwari
我必须不同意你的观点,因为当你遇到崩溃时,你的主线程似乎被锁定(semaphore_wait_trap)。 - pteofil
这就是问题所在,我的代码中没有任何地方锁定主线程。因此,看起来它被 CoreMotion 中的某个地方锁定了。而且这个崩溃不是看门狗超时。你确定这种崩溃也会因为死锁而发生吗? - Yogesh Maheshwari

1
在哪里定义了“operationQueue”?没有自我引用,所以我认为它不是类级别变量。您希望将其作为类属性进行设置,并具有强引用,以便在接收更新时不会被释放。我还看到在蓝牙调用的dispatch_queue_t的函数范围变量中出现了这种情况。
内存管理可以解释不稳定的行为,但您可以尝试通过在之前出现此问题的iPad上打开其他应用程序和大量网站选项卡来使iOS更加积极。
CMMotion更新必须按照FIFO顺序工作才能准确,这可能解释了为什么在日志中看到semaphore_wait_trap。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接