gpt4 book ai didi

ios - Swift Accelerometer 与 ScrollView 不断崩溃

转载 作者:行者123 更新时间:2023-11-28 09:05:05 26 4
gpt4 key购买 nike

编辑:我简化了代码并添加了对 NSThread.isMainThread() 的调用,以查看这是否是问题所在。在下面查看更广泛的编辑

我正在开发一个相当简单的应用程序,以在暑假期间协助一位教授进行研究。该应用程序旨在根据 iPad 中的加速度计确定句子中的单词难度。

本质上,用户将倾斜 iPad,从而产生非零加速度,位于 scrollView 内的 UILabel 中的文本将滚动相应地。

这在 99% 的时间里都非常有效。在我们几乎所有的测试中,它都能完美运行,它可以毫无问题地遍历整个文本,并且没有发生任何不好的事情。然而在极少数情况下,它只是中断,抛出错误 EXC_BAD_ACCESS。我想强调的是,在极少数情况下它确实会中断,没有明显的模式,它有时会发生在滚动的中间、接近尾声或开始时。

显然,我希望该应用程序没有错误,这是一个相当重要的问题,我只是想不通,因此非常感谢您提供的任何帮助。

这是我的ScrollingLabel 类 的全部代码(错误总是发生在startScrolling 类 的末尾)。

import Foundation
import UIKit
import QuartzCore
import CoreMotion


public class ScrollingLabel {

//Instantiation of scroll view and label
var baseTextLabel:UILabel!
var baseScrollView:UIScrollView!
var frame:CGRect!


//Instantiation of accelerometer materials
var motionManager=CMMotionManager()
var queue=NSOperationQueue()
//To rectify the issue, I have changed this to:
//var queue=NSOperationQueue.mainQueue(), SEE EDIT BELOW

init(frame:CGRect) {
/*Initializes the object by calling 3 private setup functions,
each dealing one with a specific feature of the final label, and
finally calling the scroll function to activate the accelerometer
control*/
setupFrame(frame)
setupLabel()
setupScroll()
startScrolling()
}



private func startScrolling() {
//The main accelerometer control of the label
println(NSThread.isMainQueue) //THIS RETURNS TRUE

//Allows the start orientation to become default
var firstOrientation:Bool
var timeElapsed:Double=0
if letUserCreateDefaultOrientation {firstOrientation=true}
else {firstOrientation=false}
var standardAccel:Double=0

//Begins taking updates from the accelerometer
if motionManager.accelerometerAvailable{
motionManager.accelerometerUpdateInterval=updateTimeInterval
motionManager.startAccelerometerUpdatesToQueue(self.queue, withHandler: { (accelerometerData, error:NSError!) -> Void in
println(NSThread.isMainQueue) //THIS RETURNS FALSE
//Changes the input of acceleration depending on constant control variables

var accel:Double
if self.timerStarted {
timeElapsed+=Double(self.updateTimeInterval)
}
if !self.upDownTilt {
if self.invertTextMotion {accel = -accelerometerData.acceleration.y}
else {accel = accelerometerData.acceleration.y}
}
else {
if self.invertTextMotion {accel = -accelerometerData.acceleration.x}
else {accel = accelerometerData.acceleration.x}
}

//Changes default acceleration if allowed
if firstOrientation {
standardAccel=accel
firstOrientation=false
}
accel=accel-standardAccel

//Sets the bounds of the label to prevent nil unwrapping
var minXOffset:CGFloat=0
var maxXOffset=self.baseScrollView.contentSize.width-self.baseScrollView.frame.size.width

//If accel is greater than minimum, and label is not paused begin updates
if !self.pauseScrolling && fabs(accel)>=self.minTiltRequired {

//If the timer has not started, and accel is positive, begin the timer
if !self.timerStarted&&accel<0{
self.stopwatch.start()
self.timerStarted=true
}

//Stores the data, and moves the scrollview depending on acceleration and constant speed
if self.collectData {self.storeIndexAccelValues(accel,timeElapsed: timeElapsed)}
var targetX:CGFloat=self.baseScrollView.contentOffset.x-(CGFloat(accel) * self.speed)
if targetX>maxXOffset {targetX=maxXOffset;self.stopwatch.stop();self.doneWithText=true}
else if targetX<minXOffset {targetX=minXOffset}
self.baseScrollView.setContentOffset(CGPointMake(targetX,0),animated:true)
if self.baseScrollView.contentOffset.x>minXOffset&&self.baseScrollView.contentOffset.x<maxXOffset {
if self.PRIVATEDEBUG {
println(self.baseScrollView.contentOffset)
}
}
}
})
}
}

当它确实崩溃时,它发生在 startScrolling 的末尾,当时我将内容偏移量设置为目标 X。如果您需要更多信息,我很乐意提供,但作为错误发生得如此之少,我没有什么可说的,特别是什么时候发生或类似的事情......它看起来只是随机的。

编辑: 我已将代码简化为仅相关部分,并添加了我调用 NSThread.isMainQueue() 的两个位置。在 startScrolling 的第一行调用时,.isMainQueue() 返回 TRUE,但随后在 motionManager.startAccelerometerUpdatesToQueue 中调用时返回 假的

为了纠正这个问题,我将 self.queue 从一个 NSOperationQueue() 更改为 NSOperationQueue.mainQueue(),并且在进行此切换之后,第二个 .isMainThread() 调用(在 motionManager.startAccelerometerUpdatesToQueue) 现在如我们所愿返回 TRUE

最佳答案

那是很多代码。我在基于 targetX 设置内容偏移量的行中没有看到任何内容。

有两种可能性

baseScrollView 正在被释放并且是一个僵尸(不太可能,因为它看起来像您将其定义为常规(强)实例变量。)

您正在从后台线程调用 startScrolling。如果您从后台线程更新 UI 对象,则一切都将失败。您可以使用 NSThread.isMainThread() 检查它。将该代码放在您的 startScrolling 方法中,如果它返回 false,那就是您的问题。

编辑:

根据您在下面回复我的回答的评论,您从后台线程调用 startScrolling

这确实很可能是问题所在。编辑您的帖子以显示从后台线程调用的代码,包括上下文。 (您的“加速度计更新周期”代码)。

您不能从后台线程操作 UIKit 对象,因此您可能需要将 UIKit 更改包装在“加速度计更新周期”代码中,调用在主线程上运行的 dispatch_async。

编辑#2:

您终于发布了足够的信息,以便我们可以帮助您。您的原始代码让您在后台队列中接收加速度计更新。您正在从该代码执行 UIKit 调用。这是一个禁忌,这样做的结果是不确定的。它们的范围从更新持续到永远,到根本没有发生,再到崩溃。

更改代码以使用 NSOperationQueue.mainQueue() 作为处理更新的队列应该可以解决问题。

如果您需要在处理程序中进行耗时的加速度计更新处理,那么您可以继续使用之前使用的后台队列,但将 UIKit 调用包装在 dispatch_async 中:

dispatch_async(dispatch_get_main_queue())
{
//UIKit code here
}

这样,您耗时的加速度计更新代码就不会使主线程陷入困境,但 UI 更新仍会在主线程上完成。

关于ios - Swift Accelerometer 与 ScrollView 不断崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31015306/

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