简体   繁体   中英

Potential memory leak in code

In an app I'm working on, there is a requirement to periodically poll for device's data like acceleration, gyro and motion. I wrote the following class to handle all the related tasks (I also use the third-party library SOMotionDetector to detect whether the device is moving. if only then, I call the didReceiveAcceleration delegate method).

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

This works fine. But I get random crash reports saying there is a memory leak/heap corruption in the code. Since I can't attach the debugger and move about with the app running on the phone, I can't pinpoint where this is occurring.

I'd greatly appreciate any help figuring out what the problematic code might be. Is any of my code above prone to issues like retain cycles?

You directly access self in blocks, that may cause retain cycle. Try to use weak self like:

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

So does others blocks.

You have retain cycles on self . You are capturing self strongly inside your blocks but self is retaining those blocks and variables..

Example:

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

You need to use weak self or unowned self in the block's capture frame.

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

Do something like:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM