Возможная утечка памяти в коде

В приложении, над которым я работаю, требуется периодически запрашивать данные устройства, такие как ускорение, гироскоп и движение. Я написал следующий класс для обработки всех связанных задач (я также использую стороннюю библиотеку 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()
    }
}

Это прекрасно работает. Но я получаю случайные отчеты о сбоях, в которых говорится, что в коде есть утечка памяти / повреждение кучи. Поскольку я не могу подключить отладчик и перемещаться с приложением, работающим на телефоне, я не могу точно определить, где это происходит.

Я был бы очень признателен за любую помощь в выяснении проблемного кода. Склонен ли какой-либо из приведенных выше кодов к таким проблемам, как сохранение циклов?


person Isuru    schedule 24.09.2017    source источник
comment
Я не вижу ничего очевидного. Возможно, вы захотите заменить свой if let delegate = self.delegate {...} на синтаксис, который вы использовали в другом месте self.delegate?.didReci... В противном случае единственное, что я могу предложить, это использовать инструменты Xcode, в особенно тот, который называется «Утечки».   -  person ekscrypto    schedule 24.09.2017


Ответы (2)


У вас есть циклы сохранения на 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()
    }
}
person Brandon    schedule 24.09.2017

Вы напрямую обращаетесь к self в блоках, что может вызвать цикл сохранения. Попробуйте использовать слабое я, например:

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

Как и другие блоки.

person Yun CHEN    schedule 24.09.2017