2017-09-24 114 views
2

在我正在開發的應用程序中,需要定期輪詢設備的數據,如加速度,陀螺儀和運動。我編寫了以下課程來處理所有相關任務(我還使用第三方庫SOMotionDetector來檢測設備是否正在移動,如果只有這樣,我會調用didReceiveAcceleration委託方法)。代碼中潛在的內存泄漏

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 {...}」否則我唯一可以建議的是使用Xcode Instruments特別是題爲「泄漏」的報道。 – 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 selfunowned 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 
    } 
} 

所以其他人阻止。