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

다른 사람도 차단합니다.