【问题标题】:Potential memory leak in code代码中潜在的内存泄漏
【发布时间】:2023-04-01 06:55:01
【问题描述】:

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

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

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

【问题讨论】:

  • 我没有看到任何明显的东西。您可能想用您在其他地方使用的语法 self.delegate?.didReci 替换您的“if let delegate = self.delegate {...}”...否则我唯一能建议的是使用 Xcode Instruments ,尤其是标题为 Leaks 的那篇。

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


【解决方案1】:

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

【讨论】:

    【解决方案2】:

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

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

    其他人也一样。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-10-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多