代码中潜在的内存泄漏

Potential memory leak in code

在我正在开发的应用程序中,需要定期轮询设备数据,例如加速度、陀螺仪和运动。我写了下面的 class 来处理所有相关任务(我也使用第三方库 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()
    }
}

这很好用。但是我收到随机崩溃报告说代码中存在内存 leak/heap 损坏。由于我无法附加调试器并在 phone 上移动应用程序 运行,因此我无法查明发生这种情况的位置。

如果您能帮助我找出有问题的代码,我将不胜感激。我上面的任何代码是否容易出现循环保留等问题?

您直接在块中访问self,这可能会导致保留循环。尝试使用弱自我,如:

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

其他方块也是。

您在 self 上有保留周期。 您正在块中强烈捕获 selfself 保留了这些块和变量..

示例:

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