2017-09-24 9 views
2

私が取り組んでいるアプリケーションでは、加速度、ジャイロ、モーションのようなデバイスのデータを定期的にポーリングする必要があります。私は関連するすべてのタスクを処理するために次のクラスを作成しました(私はサードパーティ製のライブラリSOMotionDetectorを使ってデバイスが動いているかどうかを検出します。コード内の潜在的なメモリリーク

import CoreMotion 
import Foundation 
import SOMotionDetector 

protocol MotionManagerDelegate: class { 
    func didReceiveAcceleration(_ acceleration: (x: Double, y: Double, z: Double)) 
    func didReceiveGyro(_ gyro: (x: Double, y: Double, z: Double)) 
    func didReceiveMotion(_ motion: (x: Double, y: Double, z: Double, w: Double)) 
} 

class MotionManager: NSObject { 

    weak var delegate: MotionManagerDelegate? 

    fileprivate let motionDetector = SOMotionDetector.sharedInstance() 
    fileprivate let accelerationCaptureInterval: TimeInterval = 0.02 
    fileprivate let gyroCaptureInterval: TimeInterval = 1 
    fileprivate var lastAcceleration: (x: Double, y: Double, z: Double) = (x: 0.0, y: 0.0, z: 0.0) 
    fileprivate var isMoving: Bool = false 
    fileprivate var motionManager: CMMotionManager! 

    override init() { 
     super.init() 

     motionManager = CMMotionManager() 
     motionManager.gyroUpdateInterval = gyroCaptureInterval 
     motionManager.accelerometerUpdateInterval = accelerationCaptureInterval 
     motionManager.deviceMotionUpdateInterval = gyroCaptureInterval 
     motionDetector?.useM7IfAvailable = true 
    } 

    func startCapturing() throws { 
     motionManager.startGyroUpdates(to: OperationQueue()) { gyroData, error in 
      if let rotation = gyroData?.rotationRate { 
       let gyro = (x: rotation.x, y: rotation.y, z: rotation.z) 
       self.delegate?.didReceiveGyro(gyro) 
      } else { 
       let gyro = (x: 0.0, y: 0.0, z: 0.0) 
       self.delegate?.didReceiveGyro(gyro) 
      } 
     } 

     motionDetector?.motionTypeChangedBlock = { motionType in 
      if motionType == MotionTypeNotMoving { 
       self.isMoving = false 
      } else { 
       self.isMoving = true 
      } 
     } 
     motionDetector?.startDetection() 

     motionManager.startAccelerometerUpdates(to: OperationQueue()) { accelerometerData, error in 
      var x = 0.0 
      var y = 0.0 
      var z = 0.0 
      if let acceleration = accelerometerData?.acceleration { 
       x = acceleration.x 
       y = acceleration.y 
       z = acceleration.z 
      } 

      if self.isMoving { 
       if let delegate = self.delegate { 
        delegate.didReceiveAcceleration((x: x, y: y, z: z)) 
       } 
      } 
     } 

     motionManager.startDeviceMotionUpdates(to: OperationQueue()) { motionData, error in 
      if let quaternion = motionData?.attitude.quaternion { 
       let motion = (x: quaternion.x, y: quaternion.y, z: quaternion.z, w: quaternion.w) 
       self.delegate?.didReceiveMotion(motion) 
      } 
     } 
    } 

    func stopCapturing() { 
     motionManager.stopGyroUpdates() 
     motionManager.stopAccelerometerUpdates() 
     motionManager.stopDeviceMotionUpdates() 
     motionDetector?.stopDetection() 
    } 
} 

これは問題なく動作します。しかし、コードにメモリリークやヒープの破損があるというランダムなクラッシュレポートが出ます。私はデバッガを接続することはできませんし、携帯電話上で実行されているアプリについて移動することはできません、私はこれが発生している場所を特定することはできません。

私は、問題のあるコードが何であるかを理解する助けに感謝します。上のコードは、保持サイクルのような問題を起こしやすいでしょうか?

+0

。あなたは "if let delegate = self.delegate {...}"を別の場所で使用した構文で置き換えることができますself.delegate?.didReci ...それ以外の場合は、Xcode Instruments特にリークス(Leaks)と名付けられたものである。 – ekscrypto

答えて

3

あなたはselfにサイクルを保持しています。

例..あなたは、あなたのブロック内で強くselfをキャプチャしているが、selfは、それらのブロックと変数を保持します

class MotionManager: NSObject { 
    override init() { 
     super.init() 

     motionManager = CMMotionManager() //retains motionManager.. 
    } 

    func usage() { 
     motionManager.execute({ foo in 
      self.blah(foo); //capturing self strongly in motionManager block.. motionManager is retained by self.. retain cycle.. 
     }) 
    } 
} 

あなたがブロックのキャプチャフレームにweak selfまたはunowned selfを使用する必要があります。

class MotionManager: NSObject { 
    override init() { 
     super.init() 

     motionManager = CMMotionManager() //retains motionManager.. 
    } 

    func usage() { 
     motionManager.execute({ [weak self] (foo) in 
      self?.blah(foo); //Doesn't retain self. Fixed :D 
     }) 
    } 
} 

ような何かを:私は明らかに何も表示されません

class MotionManager: NSObject { 

    weak var delegate: MotionManagerDelegate? 

    fileprivate let motionDetector = SOMotionDetector.sharedInstance() 
    fileprivate let accelerationCaptureInterval: TimeInterval = 0.02 
    fileprivate let gyroCaptureInterval: TimeInterval = 1 
    fileprivate var lastAcceleration: (x: Double, y: Double, z: Double) = (x: 0.0, y: 0.0, z: 0.0) 
    fileprivate var isMoving: Bool = false 

    fileprivate var motionManager: CMMotionManager! 


    override init() { 
     super.init() 

     motionManager = CMMotionManager() 
     motionManager.gyroUpdateInterval = gyroCaptureInterval 
     motionManager.accelerometerUpdateInterval = accelerationCaptureInterval 
     motionManager.deviceMotionUpdateInterval = gyroCaptureInterval 

     motionDetector?.useM7IfAvailable = true 
    } 

    func startCapturing() throws { 
     motionManager.startGyroUpdates(to: OperationQueue()) { [weak self] (gyroData, error) in 
      if let rotation = gyroData?.rotationRate { 
       let gyro = (x: rotation.x, y: rotation.y, z: rotation.z) 
       self?.delegate?.didReceiveGyro(gyro) 
      } else { 
       let gyro = (x: 0.0, y: 0.0, z: 0.0) 
       self?.delegate?.didReceiveGyro(gyro) 
      } 
     } 

     motionDetector?.motionTypeChangedBlock = { [weak self] (motionType) in 
      if motionType == MotionTypeNotMoving { 
       self?.isMoving = false 
      } else { 
       self?.isMoving = true 
      } 
     } 

     motionDetector?.startDetection() 

     motionManager.startAccelerometerUpdates(to: OperationQueue()) { [weak self] (accelerometerData, error) in 
      var x = 0.0 
      var y = 0.0 
      var z = 0.0 
      if let acceleration = accelerometerData?.acceleration { 
       x = acceleration.x 
       y = acceleration.y 
       z = acceleration.z 
      } 

      if (self?.isMoving)! { 
       if let delegate = self?.delegate { 
        delegate.didReceiveAcceleration((x: x, y: y, z: z)) 
       } 
      } 
     } 

     motionManager.startDeviceMotionUpdates(to: OperationQueue()) { [weak self] (motionData, error) in 
      if let quaternion = motionData?.attitude.quaternion { 
       let motion = (x: quaternion.x, y: quaternion.y, z: quaternion.z, w: quaternion.w) 
       self?.delegate?.didReceiveMotion(motion) 
      } 
     } 
    } 

    func stopCapturing() { 
     motionManager.stopGyroUpdates() 
     motionManager.stopAccelerometerUpdates() 
     motionManager.stopDeviceMotionUpdates() 
     motionDetector?.stopDetection() 
    } 
} 
+0

'deinit'で' stopCapturing'を呼び出すことも良いでしょう。 – Brandon

3

ブロック内でselfに直接アクセスすると、保持サイクルが発生する可能性があります。

motionDetector?.motionTypeChangedBlock = { [weak self] motionType in 
    if motionType == MotionTypeNotMoving { 
     self?.isMoving = false 
    } else { 
     self?.isMoving = true 
    } 
} 

他の人はブロックするようにしてください。

関連する問題