I have a helper class designed to get data from Core Data and pass it back to a view controller. Here's the code:
View Controller:
#interface AnnouncementsController : UIViewController
#property (nonatomic, retain) NSMutableArray *announcements;
#end
#implementation AnnouncementsController
#synthesize announcements;
- (void)viewDidLoad
{
AnnouncementCoreDataHelper *announcementHelper = [[AnnouncementCoreDataHelper alloc] init];
self.announcements = [announcementHelper selectAnnouncementsWithPredicate:#"isActive = 1" sortDescriptor:#"lastUpdated" sortAscending:NO];
[super viewDidLoad];
}
#end
And the Core Data Helper implementation:
// Select one or more announcements based on a predicate if passed in and a sort order
- (NSMutableArray*)selectAnnouncementsWithPredicate:(NSString *)predicateString sortDescriptor:(NSString *)sortBy sortAscending:(BOOL)isAscending
{
NSError *error = nil;
// Build the entity and request
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Announcement" inManagedObjectContext:managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
if(predicateString)
{
// Set the search criteria
NSPredicate *predicate = [NSPredicate predicateWithFormat:predicateString];
[request setPredicate:predicate];
}
if(sortBy)
{
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:sortBy ascending:isAscending];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
}
// Perform the search
NSMutableArray *results = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if(results == Nil)
{
NSLog(#"%#", error);
}
return results;
}
The problem is that the Core Data Helper returns two objects in its NSMutableArray results but the View Controller's NSMutableArray announcements gets nil. What is getting lost in the passing the NSMutableArray back from the helper to the controller? If I change all the variables and results to a NSArray, everything works.
I believe this was a funky issue with Xcode. I clean and reran the build and everything is working. I apologize for wasting your time and thanks for the help! I would like to know more about the memory management issues though.
Related
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 :)
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.
i want to add a NSArray to my NSMutable ( init in the appdelegate )
this is my code, the method is call each time a button (male, female, couple) is pressed so an NSArray has to be add to the MutableArray but i don't know i tried stuff but doesnt work. :
- (void) sexChoosen:(NSString *) sexe
{
NSError *err;
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Boxes" inManagedObjectContext:context];
[request setEntity:entity];
NSArray *arrayForPredicate = [context executeFetchRequest:request error:&err];
NSPredicate *sex;
sex = [NSPredicate predicateWithFormat:#"sexe = %#", sexe];
NSArray *BoxWithSex = [arrayForPredicate filteredArrayUsingPredicate:sex];
//NSMutableArray* tmp = [self createMutableArray:BoxWithSex];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSLog(#"FIRST %#", appDelegate.myArray);
// HERE IS THE CODE TO ADD THE ARRAY TO MY NSMUTABLE ARRAY = AppDelegate *appDelegate
NSLog(#"SECOND %#", appDelegate.myArray);
}
From the NSMutableArray documentation:
"addObjectsFromArray: Adds the objects contained in another given array
to the end of the receiving array’s content."
i need some information about memory management with NSManagedObjectContext objects.
I`m programming on an ipad-App and i work with core data objects.
My UIApplicationDelegate controls the NSManagedObjectContext:
- (NSManagedObjectContext *)managedObjectContext {
if (__managedObjectContext != nil)
{
return __managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil)
{
__managedObjectContext = [[NSManagedObjectContext alloc] init];
[__managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return __managedObjectContext;
}
I`m using instances of the ManagedObjectContext to display my Core Data Objects.
like this.
example table view controller:
CRMAppDelegate *appDelegate = (CRMAppDelegate*) [[UIApplication sharedApplication] delegate];
self.managedObjectContext = appDelegate.managedObjectContext;
// Initialize NSFetchedResultsController to retrieve data from the database.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityForName:#"Contact" inManagedObjectContext:self.managedObjectContext]];
// Sort results ascending by last name, first name.
NSSortDescriptor *sortDescriptorFirstLetterLastName = [[NSSortDescriptor alloc] initWithKey:#"firstLetterOfLastName" ascending:YES];
NSSortDescriptor *sortDescriptorLastName = [[NSSortDescriptor alloc] initWithKey:#"name_last" ascending:YES];
NSSortDescriptor *sortDescriptorFirstName = [[NSSortDescriptor alloc] initWithKey:#"name_first" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptorFirstLetterLastName, sortDescriptorLastName, sortDescriptorFirstName, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
[sortDescriptors release];
[sortDescriptorFirstLetterLastName release];
[sortDescriptorFirstName release];
[sortDescriptorLastName release];
/*
if ([self.managedObjectContext countForFetchRequest:fetchRequest error:NULL] == 0) {
[self reloadContactData];
}
*/
// Create NSFetched Results controller instance.
fetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:#"firstLetterOfLastName"
cacheName:#"ContactsViewCache"];
[fetchRequest release];
self.fetchedResultsController.delegate = self;
// Load data.
NSError *error;
if (![fetchedResultsController performFetch:&error]) {
// Update to handle the error appropriately.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
Is it ok to instance the NSManagedObjectContext like this? Because, the retainCount of the context is increasing til 10 while i`m testing my APP, even though i'm releasing the the context in "viewDidUnload" and "dealloc".
I`ve tried retain the context in a handler class like this:
- (NSManagedObjectContext *)getManagedObjectContext
{
NSManagedObjectContext *context = [self.managedObjectContext autorelease];
return context;
}
Whats the best way to interact with NSManagedObjectContext - objects and instances?
Thx and sry for my stuoid question :)
The value returned by retainCount is meaningless. Don't bother looking at it.
Use the Leaks instrument or heapshot analysis to determine if your app is leaking.
The is the code for the fetchRequest in viewDidLoad and the code is followed from a tutorial found here just that I'm linking the navigation controller and the tableview programmatically instead of using interface builder. The entity ProductInfo exists. However when I run the program I get the error:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '+entityForName: could not locate an NSManagedObjectModel for entity name 'ProductInfo''
I have reset the simulator incase it was an old model but the error still occurs. I have also switched to use a FetchedResultsController but the problem still persists. Is the problem because these fetchedResultsController methods aren't inside the appdelegate? They are currently in a TableViewController. How can I solve this problem?
viewDidLoad Method:
- (void)viewDidLoad{
NSFetchRequest * fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription * entity = [NSEntityDescription entityForName:#"ProductInfo" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSError * error;
self.productInfos = [_context executeFetchRequest:fetchRequest error:&error];
[fetchRequest release];
[super viewDidLoad];}
ProductInfo.h:
#class ProductDetails;
#interface ProductInfo : NSManagedObject {
#private
}
#property (nonatomic, retain) NSString * productName;
#property (nonatomic, retain) NSString * productPrice;
#property (nonatomic, retain) ProductDetails * details;
#end
FetchedResultsController
-(NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest * fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription * entity = [NSEntityDescription entityForName:#"ProductInfo" inManagedObjectContext:_context]; //line that is causing the problem
[fetchRequest setEntity:entity];
NSSortDescriptor * sort = [[NSSortDescriptor alloc] initWithKey:#"productInfos.productName" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
[fetchRequest setFetchBatchSize:20];
NSFetchedResultsController * theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext: _context sectionNameKeyPath:nil cacheName:#"Root"];
self.fetchedResultsController = theFetchedResultsController;
_fetchedResultsController.delegate = nil;
[sort release];
[fetchRequest release];
[theFetchedResultsController release];
return _fetchedResultsController;
}
Any help much appreciated.Thanks in advance.
In case the above fragments i pasted did not help, I attached the whole project with the data model inside too.
http://www.mediafire.com/?5cns4q0sv9hqn6s
This happens to me every time a switch development computers. What I have to do is
Uninstall the app from the simulator.
Rename the database file that stores the UIManagedDocument.
Perform a Build Clean.
Build and run the application.
Having run into this problem before, it sounds like it might be a simple misspelling of the entity name in the xcdatamodeld file. The string "ProductInfo" must match exactly the name of the entity in the model file.
It might also be that your context is not making the correct reference. Consider showing some more of your code related to the context if the above doesn't fix the issue.