In my root controller I have a property with the CMMotionManager
@property (strong, nonatomic) CMMotionManager *MManager;
In its getter I do lazy instantiation. When the controller's view loads, I call this method
- (void)reloadAccelerometer {
NSLog(@"Away we go");
self.MManager.deviceMotionUpdateInterval = 10.0/60.0;
[self.MManager startDeviceMotionUpdatesToQueue:self.queue withHandler:^(CMDeviceMotion *motion, NSError *error) {
NSLog(@"Y values is: %f", motion.userAcceleration.y);
}];
}
I see "Away we go" in the NSLog
and then immediately the app crashes and I get this thread log
libsystem_platform.dylib`spin_lock$VARIANT$mp:
0x39a87814: movs r1, #1
libsystem_platform.dylib`OSSpinLockLock$VARIANT$mp + 2:
0x39a87816: ldrex r2, [r0]
0x39a8781a: cmp r2, #0
0x39a8781c: it ne
0x39a8781e: bne.w 0x39a893ec ; _OSSpinLockLockSlow$shim
0x39a87822: strex r2, r1, [r0]
0x39a87826: cmp r2, #0
0x39a87828: bne 0x39a87816 ; OSSpinLockLock$VARIANT$mp + 2
0x39a8782a: dmb ish
0x39a8782e: bx lr
What's my mistake? Did i place reloadAccelerometer
in the wrong place?
I was trying to do something similar in my iOS app and spent forever trying to figure out what the cause of the crash was. This was a very cryptic (and pesky) exception. I eventually figured it out after reading through the crash reports that OSSpinLock
has to do with a thread / queue management issue.
The NSOperationQueue
is the culprit here. Your code doesn't show how you were creating your NSOperationQueue
, but I assume it was something like this:
NSOperationQueue *aQueue = [[NSOperationQueue alloc] init]; // Do NOT do this
[self.MManager startDeviceMotionUpdatesToQueue:aQueue withHandler:^(CMDeviceMotion *motion, NSError *error) {
NSLog(@"Y values is: %f", motion.userAcceleration.y);
}];
It turns out that this is not the way to use the NSOperationQueue
. That aQueue
object is the cause of the crash.
To properly setup the operation queue and avoid a crash, you should move your CMMotionManager to a different thread. Then tell NSOperationQueue to use the currentQueue
, NOT the mainQueue
. Apple recommends that it is not run on the mainQueue
, however if your app is currently running in the main queue then I don't see how the currentQueue
is any different. I tried moving the code below to a different queue using GCD, but no code was ever called.
Here's what your final code should look like:
// Check if Motion / Location services are available
if (motionManager.deviceMotionAvailable == YES && motionManager.accelerometerAvailable == YES) {
NSLog(@"Away we go");
self.MManager.deviceMotionUpdateInterval = 10.0/60.0;
[self.MManager startDeviceMotionUpdatesToQueue:[NSOperationQueue currentQueue] withHandler:^(CMDeviceMotion *motion, NSError *error) {
NSLog(@"Y values is: %f", motion.userAcceleration.y);
}];
} else {
// Motion / Accelerometer services unavailable
}
I should also note that your creation of the CMMotionManager
property (to my knowledge) is correct with (strong, nonatomic)
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With