Can not read data using NSManagedObjectContext - objective-c

I am trying to read an array from Core Data. My code is as follows
-(NSArray *)getAges
{
NSArray *fetchedObjects = [[NSArray alloc] init];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSManagedObjectContext *context = nil;
context = [[NSManagedObjectContext alloc] init];
#try {
NSPersistentStoreCoordinator *tempCordinator = (NSPersistentStoreCoordinator *)[(AppDelegate *)[[UIApplication sharedApplication] delegate] persistentStoreCoordinator];
[context setPersistentStoreCoordinator:tempCordinator];
NSError *error;
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Age"
inManagedObjectContext:context];
[fetchRequest setEntity:entity];
fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
}
#catch (NSException *exception) {
NSLog(#"core data exception occured.");
fetchedObjects = nil;
}
[fetchRequest release];
[context release];
return fetchedObjects;
}
Now in main thread, in an IBAction method, I write following line.
[NSThread detachNewThreadSelector:#selector(loadAgesMethod) toTarget:self withObject:nil];
Problem is that this method returns me an array of 20 elements. I can see the array count 20 but in debugger, against every element of array, i see 'out of scope' and 'summary unavailable'. And when I try to fetch any object from this array, i get nothing. One thing is to mention is that I am calling this method getAges in loadAgesMethod from a secondary thread as I mentioned above. This is my requirement. Also when I try to retrieve this array from main thread, i get all data perfectly ok.
Can anyone please give me a hint, where I am wrong?
Best regards

Every NSManagedObject has a NSManagedObjectContext property. This is used to lazily fetch properties of the object when required in the future. At the end of this method you call [context release] hence the NSManagedObjectContext will be deallocated and all your NSManagedObject context properties will be nil for your Age objects.
You could call [context autorelease] on the context so that it persists for some time after the method and allows you to fetch properties on the objects at a later stage. Note this will only persist until the autorelease pool drains at the end of the run loop. I am still trying myself to come up with a better solution to multithreaded fetching.

Related

I insert my CoreData in background Context and when I fetch, it doesn't show the data

I am trying to do my Core-data operation on background context.
I insert all my data on the background context but when I fetch on main thread, it doesnt show any data which I already inserted.
I dont know, where am I doing the mistake...:(
Any help appreciate.
This Is what I have tried for insert and the second one is for fetch the data.
- (void)insertNameAndSurnameInDataBase:(NSString *)name surname:(NSString *)surname numbers:(NSArray *)numbers labels:(NSArray *)labels {
AppDelegate *appDel=(AppDelegate *)[UIApplication sharedApplication].delegate;
NSManagedObjectContext *saveObjectContext = [appDel saveManagedObjectContext];
NSManagedObjectContext *bgContext = [[NSManagedObjectContext alloc]initWithConcurrencyType:NSPrivateQueueConcurrencyType];
bgContext.parentContext = saveObjectContext;
[bgContext performBlockAndWait:^{
NSError *error;
NameAndSurname *nameAndSurname = [NSEntityDescription insertNewObjectForEntityForName:#"NameAndSurname" inManagedObjectContext:bgContext];
NSString *nSurname = [NSString stringWithFormat:#"%# %#",name,surname];
if (nSurname.length == 0) {
nameAndSurname.nameSurname = [numbers objectAtIndex:0];
} else {
nameAndSurname.nameSurname = nSurname;
}
if ([bgContext save:&error]) {
} else {
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"NameAndSurname" inManagedObjectContext:bgContext];
// predicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"nameSurname =[c] %#", nSurname];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:predicate];
[fetchRequest setReturnsObjectsAsFaults:NO];
NSArray *fetchedObjects = [bgContext executeFetchRequest:fetchRequest error:&error];
if([fetchedObjects count] > 0){
[numbers enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
id obj2 = [labels objectAtIndex:idx];
obj = [obj stringByReplacingOccurrencesOfString:#" " withString:#""];
ContactNumber *phoneNumber = [NSEntityDescription insertNewObjectForEntityForName:#"ContactNumber" inManagedObjectContext:bgContext];
phoneNumber.number = obj;
phoneNumber.label = obj2;
phoneNumber.nameAndSurname = fetchedObjects[0];
NSError *error;
if ([bgContext save:&error]) {
} else {
}
}];
}
}];
}
- (NSArray *)fetchNameAndSurname {
AppDelegate *appDel = (AppDelegate *)[UIApplication sharedApplication].delegate;
_managedObjectContext = [appDel managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"NameAndSurname"
inManagedObjectContext:_managedObjectContext];
[fetchRequest setSortDescriptors:#[[[NSSortDescriptor alloc] initWithKey:#"nameSurname" ascending:YES selector:#selector(localizedCaseInsensitiveCompare:)]]];
[fetchRequest setEntity:entity];
[fetchRequest setReturnsObjectsAsFaults:NO];
NSError *error = nil;
NSArray *fetchedObjects = [_managedObjectContext executeFetchRequest:fetchRequest error:&error];
return [fetchedObjects valueForKey:#"nameSurname"];
}
The mistake you are making is to not test the many little pieces of this code which could be not working as expected. To troubleshoot something like this, you must start at the beginning and test one piece at a time. The first issue I see is confusion between managed object contexts…
It appears that the method saveManagedObjectContext is a getter for your main managed object context. In English, save is a verb, so that this method name implies that it is saving some managed object context. If I am correct, you should change the name saveManagedObjectContext to maybe mainMangaedObjectContext.
The statement _managedObjectContext = [appDel managedObjectContext] is quite nonstandard, assigning to an instance variable from what appears to be its getter. If, as usual, _managedObjectContext is the instance variable backing the property managedObjectContext, this statement has no effect.
In your first method you use [appDel saveManagedObjectContext], and in your second method you use [appDel managedObjectContext]. It looks like these should be the same context in order for your fetch to work. Are they?
UPDATE:
As you stated in your comment, that fixes the saving, but now you have a performance problem – saving to the persistent store on disk blocks the user interface, which is what your original code was trying to fix.
This is a solved problem. The solution is that your main-thread context, which interfaces the user, should be a child of your background-thread context which interfaces to the persistent store. It is explained nicely with code in this 2015 blog post by Marcus Zarra. For further reading, Chad Wilken has published a slight variation. Both are written in Objective-C for you :)

core-data is getting old data and not the updated values

I am having this strange issue which has been plaguing me all day, i am updating an object in a background thread which is saving correctly
NSFetchRequest *fetchRequest1 = [[NSFetchRequest alloc] init];
NSEntityDescription *entity1 = [NSEntityDescription entityForName:#"STMilestone"
inManagedObjectContext:tmpContext];
[fetchRequest1 setEntity:entity1];
[fetchRequest1 setReturnsObjectsAsFaults:NO];
NSError *error;
NSString *idNum = [obj valueForKey:#"id"];
// NSUInteger TrackerExists = [tmpContext countForFetchRequest:fetchRequest1 error:&error];
[fetchRequest1 setPredicate:[NSPredicate predicateWithFormat:#"identiferNumber = %#", idNum]];
NSArray *logs = [tmpContext executeFetchRequest:fetchRequest1 error:&error];
NSManagedObject *updateObj = [logs objectAtIndex:0];
[updateObj setValue:[NSNumber numberWithInt:20] forKey:#"progress"];
When i then get this object in the same background thread this gets the correct updated value
However when i go to get this updated value on my main thread it returns the old value, then when i clear the cache and reload the app it pulls the correct data
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"STTracker"
inManagedObjectContext:context];
[fetchRequest setEntity:entity];
[fetchRequest setReturnsObjectsAsFaults:NO];
NSError *error = nil;
NSMutableArray *array = [NSMutableArray arrayWithArray:[context executeFetchRequest:fetchRequest error:&error]];
I think this may be a context issue, my background thread context is being created with this
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *tmpContext = [[NSManagedObjectContext alloc] init];
tmpContext.persistentStoreCoordinator = [appDelegate persistenceCoordinator];
and the main thread is
NSPersistentStoreCoordinator *coordinator = [self persistenceCoordinator];
if(coordinator != nil)
{
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
NSUndoManager *undoManager = [[NSUndoManager alloc] init];
[undoManager setGroupsByEvent:NO];
[_managedObjectContext setUndoManager:undoManager];
}
[_managedObjectContext setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy];
However i have updated other objects in my app and they are being pulled through, so any help or insight on why this might be happening would be great
This will not work:
NSManagedObjectContext *tmpContext = [[NSManagedObjectContext alloc] init];
You need to need to init the context with the right concurrency type and assign it the correct parent context (or persistent store coordinator). See the docs
initWithConcurrencyType:
setParentContext:
When child and parent contexts are set up correctly, you an save the child context which does not really write to the persistent store but just "pushes" the changes up to the parent context. The changes are written to the store once you save the parent context again.

Memory Management Issue with CoreData

Note: This question has been completely re-organized due to so many down votes.
I am displaying a UITableViewController using NSFetchedResults with core data.
It displays fine, but when I push a new view controller and the user uses the back button to navigate back to the UITableView its crashes with that error.
I have been battling this for weeks and have not figured out why. I can tell that its likely related to memory management but I cant find it anywhere.
Update: Using Instruments-Zombies I found out that in fact it was crashing on CoreData -prepareResultsForResultsSet. See image
Here is the code from my FetchedResultsController
- (NSFetchedResultsController *)fetchedResultsController {
NSManagedObjectContext *thecontext;
thecontext = [(Logix_AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"TimeOff" inManagedObjectContext:thecontext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"timeOffStartDate" ascending:NO];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
[fetchRequest setFetchBatchSize:20];
NSFetchedResultsController *theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:thecontext sectionNameKeyPath: #"timeOffType" cacheName:nil];
self.fetchedResultsController = theFetchedResultsController;
fetchedResultsController.delegate = self;
[sort release];
[fetchRequest release];
[theFetchedResultsController release];
return fetchedResultsController;
}
Can you copy/paste the actual error from Xcode? And backtrace of where the crash happens, if any?
You definitely should not be autoreleaseing the object returned by +imageNamed:; that object is already autoreleased.
Your comment:
iVacationTimeApp[47892:19d03] -[CFString respondsToSelector:]: message sent to deallocated instance 0x95c9140
There is your answer. You have an overreleased object. May be the image, might be something else. Turn on Zombie detection in Instruments and run again.

Saving CoreData permanently

I have been trying all afternoon to get the CoreData portion of my app working and almost have it working as it should. The problem is that the CoreData does not save appropriate value when the app gets terminated (removed from multitasking menu). The value that remains is the intitial value (in my case 0, defined as NSString) I put into the entity instead of the new value the user entered. So the issue is saving the new value PERMANENTLY after the app is closed and not showing the initial value again when the app is loaded
Entity name: Gameinfo
Attribute: score
I am not sure what I am doing wrong. If anyone could help me, it would be appreciated.
Thanks!
-(IBAction)saveTheory1
{
AppDelegate *appDelegate= [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context =
[appDelegate managedObjectContext];
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Gameinfo"];
NSError *error = nil;
NSArray *someArray = [context executeFetchRequest:request error:&error];
[[someArray objectAtIndex:0] setValue:[self showKey] forKey:#"score"]; // showKey gets the text from UITextField
}
-(IBAction)showTheory1;
{
AppDelegate *appDelegate= [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context =
[appDelegate managedObjectContext];
NSEntityDescription *entityDesc =
[NSEntityDescription entityForName:#"Gameinfo"
inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDesc];
NSManagedObject *matches = nil;
NSError *error;
NSArray *objects = [context executeFetchRequest:request
error:&error];
matches = [objects objectAtIndex:0];
NSLog (#"show us the current value: %#", [matches valueForKey:#"score"]);
}
You are not saving the value at all. You are just assigning it to the Gameinfo object. To actually save it, call save: method on managed object context:
...
[[someArray objectAtIndex:0] setValue:[self showKey] forKey:#"score"];
NSError *saveError = nil;
[[appDelegate managedObjectContext] save:&saveError];
}
It doesnt look like you are calling [context save:error] anywhere. This is what moves your changes into permanent storage.

CoreData performFetch in viewDidLoad not working

When my main view controller is loaded and it calls viewDidLoad I am performing a fetch request and retiring an array or my core data objects:
+ (NSArray *)getData {
// Fetch Data
NSError *error = nil;
if (![[[AppDelegate instance] fetchedResultsController] performFetch:&error]) {
// Update to handle the error appropriately.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
NSArray *items = [[AppDelegate instance].fetchedResultsController fetchedObjects];
return items;
}//end
For some reason, this returns an empty array when called from viewDidLoad.
If I call the same method from viewDidAppear: it works correctly and returns the NSArray of my CoreData objects.
Is there a reason why this won't work in viewDidLoad?
EDIT:
Fetched Results Controller method:
/**
* The controller the gets our results from core data
*
* #version $Revision: 0.1
*/
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
// Create and configure a fetch request
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"SiteConfig" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Create the sort descriptors array
NSSortDescriptor *sectionTitle = [[NSSortDescriptor alloc] initWithKey:#"createdDate" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sectionTitle, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Create and initialize the fetch results controller
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:#"Root"];
self.fetchedResultsController = aFetchedResultsController;
fetchedResultsController.delegate = self;
// Memory management
return fetchedResultsController;
}//end
My guess is that your view controller is part of your MainWindow.xib, and so its viewDidLoad is being called before your app delegate gets the core data context ready.
You probably want to run this in viewWillAppear anyway, so that you'll get new data if you leave the screen and return. The other option would be to ensure the app delegate responds to fetchedResultsController by getting the core data stack ready if it isn't already.