代码中潜在的内存泄漏
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
上有保留周期。
您正在块中强烈捕获 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()
}
}
在我正在开发的应用程序中,需要定期轮询设备数据,例如加速度、陀螺仪和运动。我写了下面的 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
上有保留周期。
您正在块中强烈捕获 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()
}
}