Cannot fetch data from Core Data sqlite DB
First i want to apologize for starting more than one thread about this problem but i have taken step-by-step approach and steps to solve this very irritating problem. I have spend four days now to try to solve this.
The situation: I have an application where i use a pre-populated sqlite database (106k), via Core Data. I have moved the sqlite DB to the Resources folder and being able to copy it into documents. When i test i can see that the file exist, see code example and NSLog below.
I have used example code in the delegate, see below.
Everything works perfectly in the simulator but not when i test on the device.
Here is the code from the delegate:
- (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:N开发者_JAVA技巧ULL];
}
}
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_;
}
Here is the code when i read the database:
- (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);
Here is the output:
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'
I REALLY appreciate all help i can get to solve this extremely frustrating problem :-)
UPDATE
I did put in a NSLog in the delegate to check the storeURL and got the following result when running on the external iPhone device:
storeURL: file://localhost/var/mobile/Applications/7F5FDB03-0D22-46BC-91BC-4D268EB4BBEB/Documents/FamQuiz_R0_1.sqlite
The following is when i run in the simulator, without the external iPhone device:
storeURL: file://localhost/Users/PeterK/Library/Application%20Support/iPhone%20Simulator/4.2/Applications/E27EC1A4-3A87-4A1B-97DE-D464679005BE/Documents/FamQuiz_R0_1.sqlite
The crash you are getting is not coming from the posted code. The last line in the code is:
NSLog(@"allHardArrayQ %@", allHardArrayQ);
... which prints.
Somewhere down the line from the provided code you make a call like this:
[someArray objectAtIndex:0]
... while someArray
is empty and that causes the out of range error. Most likely, someArray
is one of the three empty arrays just logged but not necessarily.
Your arrays are showing empty because (1) you are getting no returns from your fetch or (2) the values for qDiff
and idQ
aren't what or where you think they are. To confirm, add logs:
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"]];
....
... that well tell you if you are getting the data in the first place. If not then you need to look at the configuration of your Core Data stack.
More generally, I think your code demonstrates that you haven't quite conceptually grasped Core Data yet. Instead, you seem to being trying to treat Core Data as a lightweight object wrapper around SQL. This is a common misperception among those skilled at SQL but new to Core Data and it causes a lot of design issues.
Firstly, there is no reason to check for the physical presence of the store file after you have initialize the persistent store. The NSPersistentStore object in memory cannot exist without the store file so if the object is there, the file is always there.
Secondly, you are piling on a lot of additional data structures in the form of arrays. This is a common need when dealing with raw SQL but is almost always unnecessary when using Core Data. The entire point of Core Data is to manage data manually (persisting it to the disk is just an option and is not required.) All your arrays should most likely be entities in the data model and you should be using fetches and sorts to pull out specific pieces of data ordered for the immediate needs of any particular view.
It's very common to see people with a strong SQL or similar background doing way, way more work than needed to implement Core Data. It's like someone who has drive a manual transmission for year who switches to an automatic. They keep stepping on the non-existent clutch and fiddling with the gear shift.
Seems like you are not getting anything from the context, all three arrays are printed empty. And the exception seems to be thrown from out of what you ve pasted here. Frankly, there are irrelevant, redundant parts like;
//=========PREPARE CORE DATA DB===========//
if (managedObjectContext == nil) { managedObjectContext = [(FamQuiz_R0_1AppDelegate *)
...which is never used, but the main problem is i guess, you are trying to access an empty array assuming something from the context comes in there, but apparently not...
You should try it step by step and be sure that your context operations return anything... And clean the code a bit so that you can see the problem easier, or anyone who is trying to help you...
PROBLEM SOLVED
After spending yet another evening trying to debug every line in the code, without understanding, i downloaded "iPhone Explorer" and manually deleted the two instances of the DB directly on the device and after that it finally worked.
I noticed that the DB was not copied to the documents folder, there an empty DB resided that was smaller that the populated DB.
Now both DB instances is the correct ones :-) ...meaning that copy works now.
If you want the "iPhone Explorer" you will find it here: http://www.macroplant.com/iphoneexplorer/
精彩评论