我使用Core Data获得了一个SpriteKit应用。主GameScene拥有许多精灵(15个左右)。其中,有3个具有特殊特征的精灵(例如名称,颜色,类型和描述)。精灵从viewDidLoad()中的Core Data存储中获取。
import SpriteKit
import UIKit
import CoreData
import GameplayKit
class GameScene: SKScene, SKPhysicsContactDelegate {
override func didMoveToView(view: SKView) {
super.didMoveToView(view)
// Background, some other sprites loaded, set-up physics world
...
// MARK: get stored data from Core Data
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let context = appDelegate.managedObjectContext
let request = NSFetchRequest(entityName: projectEntityName)
do {
let objectList = try context.executeFetchRequest(request)
for oneObject in objectList {
let spriteNode = SKSpriteNode(color: SKColor(red:(CGFloat(oneObject.valueForKey(redKey)!.floatValue), green: CGFloat(oneObject.valueForKey(greenKey)!.floatValue), blue: CGFloat(oneObject.valueForKey(blueKey)!.floatValue), alpha: 1), size: CGSizeMake(10, 10)
// set some extensions
spriteNode.name = oneObject.valueForKey(nameKey) as! String
spriteNode.type = oneObject.valueForKey(typeKey) as! String
spriteNode.description = oneObject.valueForKey(descriptionKey) as! String
}
// helper class to show what is happening in Core Data store (just more oneObject.valueForKey calls which are printed to see what value is stored)
let projectProblem = ProjectProblem()
projectProblem.projectDescriptionProblem()
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
print("touchesBegan")
if let touch = touches.first {
touchStart = touch.locationInNode(self)
// helper class again
let projectProblem = ProjectProblem()
projectProblem.projectDescriptionProblem()
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
}
override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
print("touchesCancelled")
if let touch = touches!.first, start = touchStart {
let location = touch.locationInNode(self)
// Compute the distance between the two touches
let dx = location.x - start.x
let dy = location.y - start.y
distance = sqrt(dx*dx + dy*dy)
if distance < minSwipeDistance {
let node = nodeAtPoint(location)
if node.name == "node1" {
// helper class called again
let projectProblem = ProjectProblem()
projectProblem.projectDescriptionProblem()
let transition = SKTransition.crossFadeWithDuration(StandardSettings.SceneManager.transitionDuration)
let nextScene = NextScene(size: self.frame.size)
nextScene.scaleMode = .AspectFill
view?.presentScene(nextScene, transition: transition)
} else if node.name == "node2" {
// transition to another scene (and run helper)
} else etc....
}
}
override func touchesEnded(touches: Set<UITouch>?, withEvent event: UIEvent?) {
print("touchesEnded")
// same code as touchesCancelled
}
}
出于疑难解答的目的,我在场景中设置了一些额外的标签,正如我在此处所示,它是一个帮助程序类,用于读取存储在核心数据中的内容。我在不同的地方使用过这个助手类。这个帮助程序类的原因是其中一个数据存储记录的内容被移动到另一个条目(&#34;描述&#34;字段)。由于我没有在我的代码的这一部分设置它们的值,所以不应该改变任何内容,并且对于所有其他存储的字段,商店中没有任何变化。他们保持不变。
每当我在节点上执行触摸时,&#34;描述&#34;从数据存储中读取的字段看起来像这样(带有相应的记录条目):
1st run didMoveToView() touchesBegan() touchesCancelled/Ended()
1 "node is A" "..C", "..C" "node is A"
2 "node is B" "..A", "..A" "node is B"
3 "node is C" "..B", "..B" "node is C"
2nd run didMoveToView() touchesBegan() touchesCancelled/Ended()
1 "node is A" "..A", "C", "C" "node is C"
2 "node is B" "..B", "B", "B" "node is A"
3 "node is C" "..B" "A", "A" "node is B"
3rd run didMoveToView() touchesBegan() touchesCancelled/Ended()
1 "node is C" "C", "C", "C" "node is A"
2 "node is A" "A", "B", "B" "node is B"
3 "node is B" "B", "A", "A" "node is C"
这只是为了说明正在发生的事情,但我还没有找到一个模式,除了问题似乎是在调用touchesBegan()时开始的。 touchesBegan()下多个记录的原因是,在touchesCancelled或touchesEnded()返回结果之前,touchesBegan()上返回了1到4个结果。只有这一个属性才会被移动。
一旦didMoveToView运行,记录就是它们应该在商店中的位置。然后,这个问题有时会影响与被触摸的节点相对应的记录,有时只影响其他记录或有时全部三个。虽然我在touchesBegan()中看到了重复记录,但我之前从未在touchesCancelled()或touchesEnded()中看到任何重复记录,只是记录已移至错误的位置。我已经在我的应用程序的其他地方寻找类似的问题,但其余的是稳定的。我在模拟器和iPhone上都重现了这个问题。我还重置了模拟器并执行了清理并在应用程序上构建。仍然是同样的问题。
ProjectProblem类看起来像这样:
import UIKit
import CoreData
class ProjectProblem {
func projectDescriptionProblem () {
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let context = appDelegate.managedObjectContext
let request = NSFetchRequest(entityName: projectEntityName)
do {
let objectList = try context.executeFetchRequest(request)
for thisObject in objectList {
print("DESCRIPTION: \(thisObject.valueForKey(descriptionKey) as! String)")
}
}
catch _ as NSError {
print("descriptionError")
}
}
}
有任何想法我可以采取哪些措施进行故障排除并解决此问题?
更新
我已经进行了大量的模拟并改变了setValue
写入&#34;描述&#34;的方式。字段在其他场景中执行。我发现了两件事。
1)改变我的代码中其他地方写入的方式会带来改进。现在只有数据存储中的最后一条记录在没有调用setValue
的情况下发生变化。执行写入的场景已关闭,GameScene一旦加载就处于稳定状态,但某些地方有核心数据写入操作在后台挂起。在GameScene中调用触摸时,会触发这些触摸。根据经验,早期对核心数据的写入是在代码中完成的,越好(即在远离该场景的过渡之前)。
2)即使事情有所改善,仍然存在问题;数据存储中的最后一条记录仍然在不调用setValue
的情况下发生变化。最好的描述如下:
e.g. 5 nodes, numbered 1 to 5; each node corresponds to a different core data entry ('A', 'B', 'C', 'D', 'E', respectively).
- touch node_1 and nothing happens to the records in the data store; they stay the same which is what they should do as I don't do any new writes to the data store
- touch node_2 and 'E' is replaced in the final record (the one corresponding to node 5) with 'A'; the first 4 entries stay the same (so it looks like ('A', 'B', 'C', 'D', 'A') now)
- touch node_3 and 'A' is replaced in the final record with 'B'; again the others stay the same ('A', 'B', 'C', 'D', 'B')
- touch node_4 and 'B' is replaced in the final record with 'C' ('A', 'B', 'C', 'D', 'C')
- touch node_5, which corresponds with entry 5 and 'C' becomes 'D' ('A', 'B', 'C', 'D', 'D')
- touch any other node again and the entry in the final record stays the same ('A', 'B', 'C', 'D', 'D')
基本上,记录正在更改为与数据存储中的另一个条目相同的条目(包括它自己的条目)。它不是与被触摸的节点对应的条目,而是与之前触摸的节点对应的条目。
我怀疑写入数据存储的方式有一些延迟,代码的其他部分可以超越(例如远离场景的转换)。这可能会留下一个&#34;写线程&#34;打开某个地方。 setValue在循环do {... let objectList... for thisObject in...
完成之前保持打开状态。然后,下次在应用程序中执行操作时,将从核心数据堆栈调用这些数据。如果是这种情况,有没有人经历类似的事情,如何找到这些,或者更好的是,从代码中清除它们?