【问题标题】:Cannot fetch data from Core Data sqlite DB无法从 Core Data sqlite DB 获取数据
【发布时间】:2011-04-25 17:40:15
【问题描述】:

首先,我想就这个问题启动多个线程表示歉意,但我已经采取了逐步的方法和步骤来解决这个非常恼人的问题。我已经花了四天的时间来尝试解决这个问题。

情况:我有一个应用程序,我通过 Core Data 使用预填充的 sqlite 数据库 (106k)。我已将 sqlite DB 移动到 Resources 文件夹并能够将其复制到文档中。当我测试时,我可以看到该文件存在,请参见下面的代码示例和 NSLog。

我在委托中使用了示例代码,见下文。

一切都在模拟器中完美运行,但当我在设备上测试时却不行。

这是来自委托的代码:

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator_ != nil) {
    return persistentStoreCoordinator_;
}


NSString *storePath = [[self applicationDocumentsDirectory]      stringByAppendingPathComponent: @"FamQuiz_R0_1.sqlite"];
/*
 Set up the store.
 For the sake of illustration, provide a pre-populated default store.
 */
NSFileManager *fileManager = [NSFileManager defaultManager];
// If the expected store doesn't exist, copy the default store.
if (![fileManager fileExistsAtPath:storePath]) {
    NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:@"FamQuiz_R0_1"      ofType:@"sqlite"];
    if (defaultStorePath) {
        [fileManager copyItemAtPath:defaultStorePath toPath:storePath error:NULL];
    }
}

NSURL *storeUrl = [NSURL fileURLWithPath:storePath];

NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber   numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil]; 
persistentStoreCoordinator_ = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];

NSError *error;
if (![persistentStoreCoordinator_ addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error]) {
    // Update to handle the error appropriately.
    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    exit(-1);  // Fail
}    

return persistentStoreCoordinator_;
}

这是我读取数据库时的代码:

- (void)createQuestionsList: (NSMutableArray *)diffArray {
NSLog(@">>createQuestionsList<<");

NSFileManager *filemgr = [NSFileManager defaultManager];
NSString *docsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *filePath = [docsDirectory stringByAppendingPathComponent:@"FamQuiz_R0_1.sqlite"];
if ([filemgr fileExistsAtPath: filePath ] == YES)
    NSLog (@"\n\n\nFile exists in createQuestionList\n\n\n");
else
    NSLog (@"\n\n\nFile not foundin createQuestionList\n\n\n");

int nrOfQuestions = [[diffArray objectAtIndex:0]intValue];
int nrOfEasyPlayers = [[diffArray objectAtIndex:1]intValue];
int nrOfMediumPlayers = [[diffArray objectAtIndex:2]intValue];
int nrOfHardPlayers = [[diffArray objectAtIndex:3]intValue];
int nrOfPlayers = nrOfEasyPlayers + nrOfMediumPlayers + nrOfHardPlayers;

allHardArrayQ = [[NSMutableArray alloc]init];
allMediumArrayQ = [[NSMutableArray alloc]init];
allEasyArrayQ = [[NSMutableArray alloc]init];
allDummyQ = [[NSMutableArray alloc]init];
allJointQ = [[NSMutableArray alloc]init];
hardQ = [[NSMutableArray alloc]init];
mediumQ = [[NSMutableArray alloc]init];
easyQ = [[NSMutableArray alloc]init];

masterQuestionsArray = [[NSMutableArray alloc] initWithObjects:
                        [NSNumber numberWithBool:[[diffArray objectAtIndex:4]boolValue]],
                        [NSNumber numberWithInt:nrOfHardPlayers],
                        [NSNumber numberWithInt:nrOfMediumPlayers],
                        [NSNumber numberWithInt:nrOfEasyPlayers],
                        [NSNumber numberWithInt:nrOfQuestions],
                        nil];
NSLog(@"masterQuestionsArray %@", masterQuestionsArray);

NSError *error;

//=========PREPARE CORE DATA DB===========//
if (managedObjectContext == nil) { managedObjectContext = [(FamQuiz_R0_1AppDelegate *)
                                                           [[UIApplication sharedApplication] delegate] managedObjectContext]; }

// Define qContext
NSManagedObjectContext *qContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription 
                               entityForName:@"questions" inManagedObjectContext:qContext];
[fetchRequest setEntity:entity];

NSArray *fetchedObjects = [qContext executeFetchRequest:fetchRequest error:&error];
for (NSManagedObject *info in fetchedObjects) {
    if ([[info valueForKey:@"qDiff"] intValue] == 1) { 
        [allEasyArrayQ addObject:[info valueForKey:@"idQ"]];
    } else if ([[info valueForKey:@"qDiff"] intValue] == 2) { 
        [allMediumArrayQ addObject:[info valueForKey:@"idQ"]];
    } else if ([[info valueForKey:@"qDiff"] intValue] == 3) { 
        [allHardArrayQ addObject:[info valueForKey:@"idQ"]];
    }
}

NSLog(@"allEasyArrayQ %@", allEasyArrayQ);
NSLog(@"allMediumArrayQ %@", allMediumArrayQ);
NSLog(@"allHardArrayQ %@", allHardArrayQ);

这是输出:

2011-04-25 19:35:45.008 FamQuiz_R0_1[963:307] >>createQuestionsList<<
2011-04-25 19:35:45.021 FamQuiz_R0_1[963:307] 


File exists in createQuestionList


2011-04-25 19:35:45.031 FamQuiz_R0_1[963:307] masterQuestionsArray (
1,
0,
0,
1,
5
)
2011-04-25 19:35:45.238 FamQuiz_R0_1[963:307] allEasyArrayQ (
)
2011-04-25 19:35:45.246 FamQuiz_R0_1[963:307] allMediumArrayQ (
)
2011-04-25 19:35:45.254 FamQuiz_R0_1[963:307] allHardArrayQ (
)
2011-04-25 19:35:45.311 FamQuiz_R0_1[963:307] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[NSMutableArray objectAtIndex:]: index 0 beyond bounds for empty array'

我真的很感激我能得到的所有帮助来解决这个非常令人沮丧的问题:-)

更新

我确实在委托中放入了一个 NSLog 以检查 storeURL,并在外部 iPhone 设备上运行时得到以下结果:

storeURL: file://localhost/var/mobile/Applications/7F5FDB03-0D22-46BC-91BC-4D268EB4BBEB/Documents/FamQuiz_R0_1.sqlite

以下是我在模拟器中运行,没有外部 iPhone 设备:

storeURL: file://localhost/Users/PeterK/Library/Application%20Support/iPhone%20Simulator/4.2/Applications/E27EC1A4-3A87-4A1B-97DE-D464679005BE/Documents/FamQuiz_R0_1.sqlite

【问题讨论】:

  • 您的 NSError *error 是否显示任何问题?
  • 你能把整件事贴出来吗?创建问题列表?似乎在您粘贴到此处的最后一行之后引发了异常...

标签: iphone objective-c core-data sqlite


【解决方案1】:

您遇到的崩溃并非来自发布的代码。代码的最后一行是:

NSLog(@"allHardArrayQ %@", allHardArrayQ);

... 打印。

在提供的代码的某个地方,您可以这样调用:

[someArray objectAtIndex:0]

...而someArray 为空并导致超出范围错误。 someArray 很可能是刚刚记录的三个空数组之一,但不一定。

您的数组显示为空,因为 (1) 您没有从 fetch 中获得任何回报,或者 (2) qDiffidQ 的值不是您认为的值或位置。要确认,请添加日志:

NSArray *fetchedObjects = [qContext executeFetchRequest:fetchRequest error:&error];
NSLog(@"fetchedObjects =",fetchedObjects);
for (NSManagedObject *info in fetchedObjects) {
    NSLog(@"[info valueForKey:@"qDiff"]",[info valueForKey:@"qDiff"]); 
    NSLog(@"[info valueForKey:@"idQ"]",[info valueForKey:@"idQ"]]);
    if ([[info valueForKey:@"qDiff"] intValue] == 1) { 
        [allEasyArrayQ addObject:[info valueForKey:@"idQ"]];
    ....

...这很好地告诉您您是否首先获取数据。如果没有,那么您需要查看 Core Data 堆栈的配置。

更一般地说,我认为您的代码表明您还没有从概念上掌握核心数据。相反,您似乎试图将 Core Data 视为 SQL 的轻量级对象包装器。这是那些精通 SQL 但对 Core Data 来说是新手的常见误解,它会导致很多设计问题。

首先,在初始化持久存储之后,没有理由检查存储文件的物理存在。如果没有存储文件,内存中的 NSPersistentStore 对象就无法存在,因此如果对象存在,则文件始终存在。

其次,您以数组的形式堆积了许多额外的数据结构。这是处理原始 SQL 时的常见需求,但在使用 Core Data 时几乎总是不必要的。 Core Data 的全部意义在于手动管理数据(将数据持久化到磁盘只是一种选择,不是必需的。)所有数组都应该是数据模型中的实体,并且应该使用 fetches 和 sorts 来提取为满足任何特定视图的即时需求而订购的特定数据。

经常看到具有强大 SQL 或类似背景的人正在做的工作比实现 Core Data 所需的工作量要多得多。这就像一个开了一年手动变速箱的人换了自动变速箱。他们不断踩着不存在的离合器并摆弄变速杆。

【讨论】:

  • TechZen 非常感谢您的回答,我会尽快开始使用它。但是,一切都在模拟器中完美运行,但当我移动到 iPHONE 设备时却不行。我的结论是数据库是空的,资源中的数据库和模拟器构建中的数据库都是 106k,或者我没有指向 de 设备中的文档文件夹中的数据库。
  • 我添加了 NSLog 并且在模拟器中运行良好。当我使用 iPhone 设备运行时,我得到“0”=没有命中。再次,我的感觉是我在设备上找不到数据库?
  • 在模拟器上运行正常的原因应该是数据库文件中遗漏了数据。所以你没有在那里得到一个空数组,但是在设备上,新数据库是空的,当你试图访问数组时你会得到异常。不是模拟器vs设备,是剩余数据vs空数据库...仔细检查,删除数据库文件后尝试
  • 他正在将填充的文件从应用程序包复制到文档文件夹。我想他可以检查一下文件的大小,以确保他得到的是填充的文件,而不是几乎生成的空文件。
  • 我还要补充一点,即使只是空商店的情况,任何由于数据丢失而崩溃的代码都是需要修复的脆弱代码。
【解决方案2】:

似乎您没有从上下文中得到任何东西,所有三个数组都打印为空。异常似乎是从您在此处粘贴的内容中抛出的。坦率地说,有一些不相关的、多余的部分,比如;

//=========PREPARE CORE DATA DB===========//
if (managedObjectContext == nil) { managedObjectContext = [(FamQuiz_R0_1AppDelegate *)

...从未使用过,但我想主要问题是,您正在尝试访问一个空数组,假设上下文中的某些内容进入了那里,但显然不是...

您应该逐步尝试并确保您的上下文操作返回任何内容... 并稍微清理一下代码,以便您可以更轻松地看到问题,或者任何试图帮助您的人......

【讨论】:

    【解决方案3】:

    问题已解决

    又花了一个晚上尝试调试代码中的每一行,但我没有理解,我下载了“iPhone Explorer”并直接在设备上手动删除了数据库的两个实例,之后它终于工作了。

    我注意到数据库没有复制到文档文件夹中,存在一个比填充的数据库小的空数据库。

    现在两个数据库实例都是正确的 :-) ...这意味着副本现在可以工作了。

    如果你想要“iPhone Explorer”,你可以在这里找到它:http://www.macroplant.com/iphoneexplorer/

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-13
      • 1970-01-01
      • 2012-07-07
      • 2014-01-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多