代码中潜在的内存泄漏

时间:2017-09-24 12:47:53

标签: ios swift memory-leaks crash retain-cycle

在我正在开发的应用中,需要定期轮询设备的数据,如加速度,陀螺仪和动作。我编写了以下类来处理所有相关任务(我还使用第三方库SOMotionDetector来检测设备是否正在移动。如果只有那时,我调用didReceiveAcceleration委托方法。 / p>

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

这很好用。但我得到随机崩溃报告,说代码中存在内存泄漏/堆损坏。由于我无法连接调试器并随着手机上运行的应用移动,我无法确定这种情况发生的位置。

我非常感谢任何帮助,找出问题代码可能是什么。我的上述任何代码是否容易出现保留周期等问题?

2 个答案:

答案 0 :(得分:3)

您可以直接访问块中的self,这可能会导致保留周期。尝试使用弱自我:

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

其他人阻止。

答案 1 :(得分: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()
    }
}