加速度を取得する & データはセマフォで保護しよう
iOSで加速度を取得するのにはいくつか方法があるらしく、古くはUIAccelerometer とかでやっていたようですが、5.0以降はCoreMotionフレームワークを使えとのことなので、それに従って実装してみました。
なお、CoreMotionでの方法にも2種類あって、ひとつはCMDeviceMotionで加速度や角速度、軸傾きなどをまとめて取得するのと、CMAccelorometerを使って単体で取得するやり方があるようです。
CMDeviceMotionは複数のセンサーデータで値を補正しているため、ブレが少なくなっているみたいだけど、その分CPUを使用するので、利用目的にあわせて使い分けると良いそうです。
で、使い方は簡単。まずはクラスのインスタンス変数を宣言。
CMMotionManager* _motionMgr; // MotionManager
NSMutableData* _accData; // 加速度格納用
dispatch_semaphore_t _accSemaphore; // 保存バッファ排他制御用セマフォ
そして加速度センサー開始
_motionMgr = [[CMMotionManager alloc] init];
_motionMgr.accelerometerUpdateInterval = 0.2; // 5Hz;
if(_motionMgr.accelerometerAvailable){
[_motionMgr startAccelerometerUpdatesToQueue:[NSOperationQueue currentQueue] withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) {
if (accelerometerData && !error) {
CMAcceleration acc = accelerometerData.acceleration;
if(_accSemaphore && !dispatch_semaphore_wait(_accSemaphore, 50 * NSEC_PER_MSEC)){
[_accData appendBytes:&acc length:sizeof(acc)]; // データを一時保存
if(_accSemaphore) dispatch_semaphore_signal(_accSemaphore);
}
}
}];
}
余計なコードが付いていますが、計測したデータを数秒毎にまとめてファイルへ保存する処理を別のスレッドで行うための、同期用のセマフォの処理を入れています。GCDのセマフォはかなり負荷が小さく、NSLockよりもおすすめっぽいです。
停止は以下
if(_motionMgr){
[_motionMgr stopAccelerometerUpdates];
_motionMgr = nil;
}
ちなみに値をまとめて取り出すところは
NSMutableData* newData = [NSMutableData data];
NSData* accData = nil;
if(_accSemaphore && !dispatch_semaphore_wait(_accSemaphore, 50 * NSEC_PER_MSEC)){
accData = _accData;
_accData = newData;
if(_accSemaphore) dispatch_semaphore_signal(_accSemaphore);
}
if(accData){
// ファイルへ
}
と、セマフォ獲得中にNSMutableDataのバッファ本体を操作するのではなく、インスタンス変数の指すオブジェクト自体を切り替えてやります。セマフォの保持期間を最小限にする目的のためです。