gpt4 book ai didi

swift - 以下代码中僵尸的原因是什么

转载 作者:行者123 更新时间:2023-11-28 12:35:16 27 4
gpt4 key购买 nike

我有以下用于收集设备运动数据的类:

class MotionManager: NSObject {
static let shared = MotionManager()
private override init() {}

// MARK: - Class Variables

private let motionManager = CMMotionManager()

fileprivate lazy var locationManager: CLLocationManager = {
var locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.activityType = .fitness
locationManager.distanceFilter = 10.0
return locationManager
}()

private let queue: OperationQueue = {
let queue = OperationQueue()
queue.name = "MotionQueue"
queue.qualityOfService = .utility
return queue
}()

fileprivate var motionDataRecord = MotionDataRecord()

private var attitudeReferenceFrame: CMAttitudeReferenceFrame = .xTrueNorthZVertical

var interval: TimeInterval = 0.01
var startTime: TimeInterval?

// MARK: - Class Functions

func start() {
startTime = Date().timeIntervalSince1970
startDeviceMotion()
startAccelerometer()
startGyroscope()
startMagnetometer()
startCoreLocation()
}

func startCoreLocation() {
switch CLLocationManager.authorizationStatus() {
case .authorizedAlways:
locationManager.startUpdatingLocation()
locationManager.startUpdatingHeading()
case .notDetermined:
locationManager.requestAlwaysAuthorization()
case .authorizedWhenInUse, .restricted, .denied:
break
}
}

func startAccelerometer() {
if motionManager.isAccelerometerAvailable {
motionManager.accelerometerUpdateInterval = interval
motionManager.startAccelerometerUpdates(to: queue) { (data, error) in
if error != nil {
log.error("Accelerometer Error: \(error!)")
}
guard let data = data else { return }
self.motionDataRecord.accelerometer = data
}
} else {
log.error("The accelerometer is not available")
}

}

func startGyroscope() {
if motionManager.isGyroAvailable {
motionManager.gyroUpdateInterval = interval
motionManager.startGyroUpdates(to: queue) { (data, error) in
if error != nil {
log.error("Gyroscope Error: \(error!)")
}
guard let data = data else { return }
self.motionDataRecord.gyro = data
}
} else {
log.error("The gyroscope is not available")
}
}

func startMagnetometer() {
if motionManager.isMagnetometerAvailable {
motionManager.magnetometerUpdateInterval = interval
motionManager.startMagnetometerUpdates(to: queue) { (data, error) in
if error != nil {
log.error("Magnetometer Error: \(error!)")
}
guard let data = data else { return }
self.motionDataRecord.magnetometer = data
}
} else {
log.error("The magnetometer is not available")
}
}

func startDeviceMotion() {
if motionManager.isDeviceMotionAvailable {
motionManager.deviceMotionUpdateInterval = interval
motionManager.startDeviceMotionUpdates(using: attitudeReferenceFrame, to: queue) { (data, error) in
if error != nil {
log.error("Device Motion Error: \(error!)")
}
guard let data = data else { return }
self.motionDataRecord.deviceMotion = data
self.motionDataRecord.timestamp = Date().timeIntervalSince1970
self.handleMotionUpdate()
}
} else {
log.error("Device motion is not available")
}
}

func stop() {
locationManager.stopUpdatingLocation()
locationManager.stopUpdatingHeading()
motionManager.stopAccelerometerUpdates()
motionManager.stopGyroUpdates()
motionManager.stopMagnetometerUpdates()
motionManager.stopDeviceMotionUpdates()
}

func handleMotionUpdate() {
print(motionDataRecord)
}

}

// MARK: - Location Manager Delegate
extension MotionManager: CLLocationManagerDelegate {

func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedAlways || status == .authorizedWhenInUse {
locationManager.startUpdatingLocation()
} else {
locationManager.stopUpdatingLocation()
}
}

func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else { return }
motionDataRecord.location = location
}

func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) {
motionDataRecord.heading = newHeading
}

}

但是我在运行一段时间后得到 EXC_BAD_ACCESS。我运行了僵尸工具,看起来 handleMotionUpdate() 是错误的调用者。而 MotionDataRecord 或它的某些属性正在以某种方式被释放...

MotionDataRecord 是一个struct:

struct MotionDataRecord {
var timestamp: TimeInterval = 0
var location: CLLocation?
var heading: CLHeading?
var motionAttitudeReferenceFrame: CMAttitudeReferenceFrame = .xTrueNorthZVertical
var deviceMotion: CMDeviceMotion?
var altimeter: CMAltitudeData?
var accelerometer: CMAccelerometerData?
var gyro: CMGyroData?
var magnetometer: CMMagnetometerData?
}

知道这里发生了什么吗?

编辑:

已将项目的精简版本添加到 github here

编辑:

僵尸工具截图:

zombies instrument screenshot

最佳答案

好吧,我将尝试做一个小的思想实验来暗示这里可能发生的事情。

首先要记住以下几点:

  • 您的 MotionDataRecord 是一个几乎完全由引用类型实例属性组成的结构。这会强制该结构参与引用计数。

  • 您正在不同线程上疯狂访问此结构的属性。您的 locationManager:didUpdateLocations: 在主线程上设置 motionDataRecord.location,而例如您的 motionManager.startDeviceMotionUpdates 在后台线程(queue)上设置了 motionDataRecord.deviceMotion

  • 每次设置结构属性时,都会改变结构。但实际上在 Swift 中并没有 struct mutation 这样的东西:struct 是一种值类型。真正发生的是整个结构被复制和替换(僵尸日志中的 initializeBufferWithCopyOfBuffer)。

好的,那么在多个并发线程上,您将进入并替换您的 struct-full-of-references。每次您这样做时,一个结构副本就会消失,而另一个会出现。这是一个充满引用的结构,因此这涉及到引用计数。

假设这个过程是这样的:

  1. 创建新结构。

  2. 通过复制引用将新结构的引用属性设置为旧结构的引用属性(我们正在更改的除外)。这里有一些保留和释放,但它们都平衡了。

  3. 设置我们要替换的新结构的引用属性。这会保留新值并释放旧值。

  4. 将新结构交换到位。

但这些都不是原子。因此,这些步骤可能会乱序运行,相互交错,因为(记住)您有多个线程同时访问该结构。所以想象一下,在另一个线程上,我们在步骤 3 和 4 之间访问结构。特别是,在一个线程的步骤 3 和 4 之间,我们在另一个线程上执行步骤 1 和 2。在那一刻,旧结构仍然存在,它对我们要替换的属性的引用指向垃圾(因为它在第 3 步的第一个线程中被释放和释放)。我们尝试在垃圾属性上进行复制。崩溃。

因此,简而言之,我建议 (1) 使 MotionDataRecord 成为一个类而不是结构,并且 (2) 理顺线程(至少,在您之前进入 CMMotionManager 回调中的主线程触摸 MotionDataRecord)。

关于swift - 以下代码中僵尸的原因是什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41069364/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com