2017-09-24 3 views
2

Dans une application sur laquelle je travaille, il est nécessaire d'interroger périodiquement les données de l'appareil telles que l'accélération, le gyroscope et le mouvement. J'ai écrit la classe suivante pour gérer toutes les tâches connexes (j'utilise également la bibliothèque tierce SOMotionDetector pour détecter si le périphérique est en mouvement, si seulement j'appelle la méthode de délégué didReceiveAcceleration).Fuite de mémoire potentielle dans le code

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() 
    } 
} 

Cela fonctionne très bien. Mais je reçois des rapports d'erreur aléatoires disant qu'il y a une fuite de mémoire/corruption de tas dans le code. Comme je n'arrive pas à joindre le débogueur et à me déplacer avec l'application en cours d'exécution sur le téléphone, je n'arrive pas à savoir où cela se produit.

J'apprécierais énormément toute aide pour trouver ce que pourrait être le code problématique. Est-ce que l'un de mes codes ci-dessus est sujet à des problèmes comme les cycles de retenue?

+0

Je ne vois pas quelque chose d'évident. Vous pouvez remplacer votre "if let delegate = self.delegate {...}" par la syntaxe que vous avez utilisée ailleurs self.delegate? .didReci ... Sinon, la seule chose que je peux suggérer est d'utiliser les Xcode Instruments , en particulier celui intitulé Leaks. – ekscrypto

Répondre

3

Vous avez des cycles de retenue sur self. Vous capturez self fortement dans vos blocs mais self retient les blocs et les variables ..

Exemple:

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.. 
     }) 
    } 
} 

Vous devez utiliser weak self ou unowned self dans le cadre de la capture du bloc.

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 
     }) 
    } 
} 

faire quelque chose comme:

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

Cela peut aussi être une bonne idée d'appeler 'stopCapturing' dans' deinit'. – Brandon

3

Vous accédez directement aux blocs self, ce qui peut provoquer un cycle de rétention. Essayez d'utiliser le soi faible comme:

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

De même que d'autres blocs.