iOS swift 应用中 Core Data 的奇怪行为
Strange behavior from Core Data in iOS swift app
我正在为实体分配非零值,保存它们,然后立即检索它们。但是这些值返回为零。这是 iOS 8.1 和 Xcode(最新的 GA 版本)上的 Swift,在模拟器和设备上具有相同的行为。
我有三个实体:Match
、Score
和 Team
。每个 Match
有两支球队,team1
和 team2
。 Score
还有 team1
和 team2
。 Match
与 Score
具有 1x1 关系,字段分别为 bet
和 betFor
。 Match
由其字段 matchNumber
标识。
所以我有以下代码:
var match:CBMatch?
var betCard:CBScore?
for matchNum in 1...42 {
match = CBMatch.lookup(self.managedObjectContext!, matchNumber: matchNum)
betCard = CBScore.addForBet(self.managedObjectContext!, match: match!)
betCard!.team1 = match!.team1
betCard!.team2 = match!.team2
match!.bet = betCard!
println("For match \(matchNum), \(betCard!.team1.shortname) vs \(betCard!.team2.shortname)")
// This is fine for all matchNum values
}
// Verify
if true {
var err:NSError? = nil
self.managedObjectContext!.save(&err)
if err != nil {
abort() // Hasn't happened yet
}
match = CBMatch.lookup(self.managedObjectContext!, matchNumber: 1)
betCard = match!.bet
println("For match 1, \(betCard!.team1.shortname) vs \(betCard!.team2.shortname)")
// Crashes with NPE
// It complains that betCard!.team2 is nil but betCard!.team1 is fine
}
基本上,尝试检索我刚刚设置的信息时返回的结果为 nil。
CBMatch:lookup
和 CBScore:addForBet
是微不足道的:
class func lookup(moc: NSManagedObjectContext, matchNumber: Int) -> CBMatch? {
var fetchRequest = NSFetchRequest(entityName: "CBMatch")
var predicate = NSPredicate(format: "matchNumber == %d", matchNumber)
fetchRequest.fetchLimit = 1
fetchRequest.predicate = predicate
// fetchRequest.returnsObjectsAsFaults = false
// With or without didn't make a difference
var err:NSError? = nil
let matches = moc.executeFetchRequest(fetchRequest, error: &err)!
if matches.count == 1 && err == nil {
let fetchedMatch = matches[0] as CBMatch
return fetchedMatch
}
println("Could not find match \(matchNumber)")
return nil
}
class func addForBet(moc: NSManagedObjectContext, match:CBMatch) -> CBScore {
var entity = NSEntityDescription.insertNewObjectForEntityForName("CBScore", inManagedObjectContext: moc) as CBScore
entity.betFor = match
return entity
}
我不能通过简单地丢弃 bet.team1 等来简化数据模型,因为在某些情况下 bet.team1 和 match.team1 会不同,我需要能够比较它们。对于这种(最简单的)情况,它们是相同的,因为这是第一次初始化 Score 对象。
我已经尝试了几种方法,包括在获取期间 returnsObjectsAsFault = false 等。当我使用 sqlite3 打开 .sqlite 文件时,我可以看到正在创建的条目。我尝试使用 sql 调试标志设置为 3,但没有跳出来。我不是说大道已尽,但我确实是。
对这里可能发生的事情有什么想法吗?我坚持这个很长一段时间,任何想法将不胜感激。
错误是 EXC_BAD_ACCESS
,问题是 match.bet.team1
是 nil 。这与我在上面几行中所做的相同。
在我看来,您的数据模型存在缺陷。你两次引用每个团队,这真的没有必要。 Bet
实体不需要了解团队,因为它与 Match
实体具有一对一的关系。 Bet
可能包含类似于 firstValue
和 secondValue
的内容。
虽然理论上您的设置也应该有效,但您引入了不必要的复杂性。您没有提供足够的信息来解决您的错误,但我认为甚至没有必要费心去寻找那个错误。
只需使用 match.team1
而不是 match.bet.team1
。
顺便说一句,很明显要注意 match
是 nil
,score
是 nil
,或者 match.team1
是 nil
- 在所有情况下 score.team1
也是 nil
.
在我的代码中发现了错误 - team1 和 team2 的关系是 1:1,但为了允许多次分配,它们需要 1:N。
我正在为实体分配非零值,保存它们,然后立即检索它们。但是这些值返回为零。这是 iOS 8.1 和 Xcode(最新的 GA 版本)上的 Swift,在模拟器和设备上具有相同的行为。
我有三个实体:Match
、Score
和 Team
。每个 Match
有两支球队,team1
和 team2
。 Score
还有 team1
和 team2
。 Match
与 Score
具有 1x1 关系,字段分别为 bet
和 betFor
。 Match
由其字段 matchNumber
标识。
所以我有以下代码:
var match:CBMatch?
var betCard:CBScore?
for matchNum in 1...42 {
match = CBMatch.lookup(self.managedObjectContext!, matchNumber: matchNum)
betCard = CBScore.addForBet(self.managedObjectContext!, match: match!)
betCard!.team1 = match!.team1
betCard!.team2 = match!.team2
match!.bet = betCard!
println("For match \(matchNum), \(betCard!.team1.shortname) vs \(betCard!.team2.shortname)")
// This is fine for all matchNum values
}
// Verify
if true {
var err:NSError? = nil
self.managedObjectContext!.save(&err)
if err != nil {
abort() // Hasn't happened yet
}
match = CBMatch.lookup(self.managedObjectContext!, matchNumber: 1)
betCard = match!.bet
println("For match 1, \(betCard!.team1.shortname) vs \(betCard!.team2.shortname)")
// Crashes with NPE
// It complains that betCard!.team2 is nil but betCard!.team1 is fine
}
基本上,尝试检索我刚刚设置的信息时返回的结果为 nil。
CBMatch:lookup
和 CBScore:addForBet
是微不足道的:
class func lookup(moc: NSManagedObjectContext, matchNumber: Int) -> CBMatch? {
var fetchRequest = NSFetchRequest(entityName: "CBMatch")
var predicate = NSPredicate(format: "matchNumber == %d", matchNumber)
fetchRequest.fetchLimit = 1
fetchRequest.predicate = predicate
// fetchRequest.returnsObjectsAsFaults = false
// With or without didn't make a difference
var err:NSError? = nil
let matches = moc.executeFetchRequest(fetchRequest, error: &err)!
if matches.count == 1 && err == nil {
let fetchedMatch = matches[0] as CBMatch
return fetchedMatch
}
println("Could not find match \(matchNumber)")
return nil
}
class func addForBet(moc: NSManagedObjectContext, match:CBMatch) -> CBScore {
var entity = NSEntityDescription.insertNewObjectForEntityForName("CBScore", inManagedObjectContext: moc) as CBScore
entity.betFor = match
return entity
}
我不能通过简单地丢弃 bet.team1 等来简化数据模型,因为在某些情况下 bet.team1 和 match.team1 会不同,我需要能够比较它们。对于这种(最简单的)情况,它们是相同的,因为这是第一次初始化 Score 对象。
我已经尝试了几种方法,包括在获取期间 returnsObjectsAsFault = false 等。当我使用 sqlite3 打开 .sqlite 文件时,我可以看到正在创建的条目。我尝试使用 sql 调试标志设置为 3,但没有跳出来。我不是说大道已尽,但我确实是。
对这里可能发生的事情有什么想法吗?我坚持这个很长一段时间,任何想法将不胜感激。
错误是 EXC_BAD_ACCESS
,问题是 match.bet.team1
是 nil 。这与我在上面几行中所做的相同。
在我看来,您的数据模型存在缺陷。你两次引用每个团队,这真的没有必要。 Bet
实体不需要了解团队,因为它与 Match
实体具有一对一的关系。 Bet
可能包含类似于 firstValue
和 secondValue
的内容。
虽然理论上您的设置也应该有效,但您引入了不必要的复杂性。您没有提供足够的信息来解决您的错误,但我认为甚至没有必要费心去寻找那个错误。
只需使用 match.team1
而不是 match.bet.team1
。
顺便说一句,很明显要注意 match
是 nil
,score
是 nil
,或者 match.team1
是 nil
- 在所有情况下 score.team1
也是 nil
.
在我的代码中发现了错误 - team1 和 team2 的关系是 1:1,但为了允许多次分配,它们需要 1:N。