2017-09-24 160 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 
    } 
} 

所以其他人阻止。