How to pass ManagedObjectContext to TabBarViewControllers - iphone

After reading quite a couple of questions in stackoverflow, I still can't find an answer at the moment.
I have trouble passing ManagedObjectContext from appdelegate to my tabbarcontroller view.
in my appdelegate.m I have this
#import "memoView.h"
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
memoView *mView = (memoView *)navigationController.topViewController;
mView.ObjectContext =[self managedObjectContext];
}
in memoView.m
- (void)viewDidLoad
{
[super viewDidLoad];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"NoteLog" inManagedObjectContext:[self ObjectContext]];
[fetchRequest setEntity:entity];
NSError *error;
self.memoInfo = [ObjectContext executeFetchRequest:fetchRequest error:&error];
//self.title = #"Memo";
[fetchRequest release];
}
error reason: '+entityForName: could not locate an NSManagedObjectModel for entity name 'NoteLog''
I'm not sure what I have done wrong, I'm relative new to core data.
Any comments are appreciated.

I ran into this at one point and solved like this:
YourAppDelegate *appDelegate = (YourAppDelegate *)[[UIApplication sharedApplication] delegate];
self. ObjectContext = appDelegate.ManagedObjectContect;

Where do you put following code? I guess mView is nil when you assigning mView.ObjectContext:
#import "memoView.h"
memoView *mView = (memoView *)navigationController.topViewController;
mView.ObjectContext =[self managedObjectContext];

I'd personally implement a Singleton for my CoreData Stack. - But in a slightly safer manner like here.
The singleton pattern is basically an excuse for a "global variable". Although if implemented correctly and used wisely one of the most powerful patterns there are. Simply speaking, what it does is: It creates an object of a class IF it doesn't exist yet and stores it in a static variable of that object's class. And since a class variable's content is the same amongst all the instances of that class (obviously), the next time you try to allocate an instance of the class it checks whether it was allocated before, and if it was returns the old instance.
Thus, you can basically "allocate" from anywhere and however often you want and you'll always get the same object back.
People tend to then call that singleton class something along the lines of "DataManager" or similar.

Related

refresh reference ManagedObjectContext

in my iOS app i have a core data, and i have notice that sometime, in a specific view, when i retrieve information from core data, are not always up to date, i'm explain well:
if i update some value in the core data, and then i go in in a specific view to view this information, that information are not up to date, now i show how i access my database:
.h
#property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;
.m
#synthesize managedObjectContext;
- (NSArray *)sortInformation{
if (managedObjectContext == nil) {
managedObjectContext = [(AppDelegate *) [[UIApplication sharedApplication] delegate] managedObjectContext];
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"MyEntity" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
....
and then i display my information in a table view, all work perfectly, there is only this problem, that SOME TIME seems that the update i have done in another view is not read in this view, but if i close the app, and i close it from the background, and then i reopen it all works fine...so i have saved the update in the core data correctly, so i think the problem is in this view, maybe i have an old reference of the core data and not the update version, maybe the problem is this:
if (managedObjectContext == nil) {
managedObjectContext = [(AppDelegate *) [[UIApplication sharedApplication] delegate] managedObjectContext];
}
that refresh only if the variable managedObjectContext is nil so only if the view is deallocated...so never, because is one of my root view controller in a UITabbarController, so my question is, how i can access to the core data to have always a update version of it?
no need to refresh the context just call save method on managedObjectContext like [managedObjectContext save];
or if you are using more than one managed object context you should merge changes done by the context
On the implementation of the database class you can do like this
-(id) initWithContext: (NSManagedObjectContext *)managedObjContext {
self = [super init];
[self setManagedObjectContext:managedObjContext];
return self;
}
the managedObjContext is pass and set
On your app delegate when call the database class it should be something like this
database = [[Database alloc] initWithContext:self.managedObjectContext];
Then you are good to accessed the database like this
- (NSArray *)sortInformation {
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"MyEntity" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSMutableArray *mutableFetchResults = [[[managedObjectContext_ executeFetchRequest:request error:&error] mutableCopy] autorelease];
[request release];
return mutableFetchResults;
}

Getting SIGABRT when executing fetchRequest

I'm using Xcode 4.2 and iOS SDK 5.0 and Apple's Master-Detail Application Template for iPad with Core Data. It's similar to the "Locations" sample code. I've successfully managed to pass the managed object context (MOC) from the MasterViewController (MVC) to the DetailViewController (DVC). The detail view controller accepts input from the user in some text boxes and stores that in core data; this part works like a charm. Now, I have a ActionViewController (AVC) that is a Popover View that is supposed to allow the user to e-mail all of the data in the MOC if they choose so. However, when trying to do a fetch I get a SIGABRT. I used breakpoints to pinpoint exactly where:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//Fetch the crosses
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Event"
inManagedObjectContext:context];
[fetchRequest setEntity:entity]; // Where it crashes
//Other code
}
Also, in the debugger I see that the *context pointer is 0x0 which I'm guessing means that it's not there?
The way I made the MVC pass the MOC was like this:
// Pass the managedObjectContext to the DetailViewController as a property
_detailViewController.managedObjectContext = self.managedObjectContext;
//Pass the MOC to the actionViewController
_actionViewController.managedObjectContext = self.managedObjectContext;
I've looked at other posts and it seems that passing the MOC to all the view controllers from the AppDelegate may be the way to go but I wanted to find out why the data input works for the DVC but crashes with fetching data in the AVC. What grand error have I made?
Fixed link to github project: https://github.com/scottdaniel/fly_punnett
So I figured it out, though I'm probably breaking some "good programming" maxim...
All I did was delete
//Pass the MOC to the actionViewController
_actionViewController.managedObjectContext = self.managedObjectContext;
along with all other connections from AVC to MVC (i.e. #class ActionViewController, #property *ActionViewController in the MCV interface) << ask me for the full details if you want them or check it out on github (link above)
Then I just added
#import AppDelegate.h
to my ActionViewController.m
and modified the MOC creation like so:
-(void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//Fetch the crosses
NSManagedObjectContext *context = [(AppDelegate *)[[UIApplication
sharedApplication] delegate]
managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Event"
inManagedObjectContext:context];
The MOC is created successfully and I'm able take the data for the user and put it into an e-mail they can send to whoever.

Memory leak problem and i need help #2 [duplicate]

I am very new at this and seems to have a leak in this piece of code that i cannot fix:
The Instruments shows on this line with a 100%:
NSMutableArray *read_Question = [[NSMutableArray alloc] initWithCapacity: 0];
I have tried all kind of things but not been able to fix it.
Anyone nice that can advice me how to proceed?
- (NSMutableArray *)readQuestion: (int)questionNr {
NSMutableArray *read_Question = [[NSMutableArray alloc] initWithCapacity: 0];
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:#"idQ"] intValue] == questionNr) {
[read_Question addObject:[info valueForKey:#"question"]];
[read_Question addObject:[info valueForKey:#"qRightAnswer"]];
[read_Question addObject:[info valueForKey:#"qWrongAnswer1"]];
[read_Question addObject:[info valueForKey:#"qWrongAnswer2"]];
}
}
[fetchRequest release];
return [read_Question autorelease];
}
It seams that you are returning the object only inside the if statement. Meaning that if the if statement is false you will not autorelease the array. Or maybe you didn't paste the entire method. Let me know. Instruments is sometimes tricky.
This is a dupe of your other question Memory leak problem and i need help #1
When i did release i got into trouble,
of course. I did try to change the
names on the three and do release so
there was unique names but that did
not work.
Changing the names across three different files? That won't do anything and it indicates that you haven't entirely wrapped your head around objects, pointers, and memory management.
The Objective-C and Memory Management guides will help.
Could this be the reason for the leak
i have in this .m file?
Nope -- as I answered in the other question, the leak is most likely because you retain the object that is returned by that method and then don't release it anywhere.
Instruments is telling you were the leaked object was allocated, not where it was necessarily leaked.
While you may not be autoreleasing the array in all cases on return from that method, you might also be retaining it somewhere else and not balancing that retain with a release.
I am assuming you set the property managedObjectContext to "retain". Change the line to this (include "self" so that it gets retained):
if (self.managedObjectContext == nil) { self.managedObjectContext = [(FamQuiz_R0_1AppDelegate *)
[[UIApplication sharedApplication] delegate] managedObjectContext]; }
Then add your release back in.
Since I believe the code from picciano will fix the issue of the openingsposter, here a small explanation why it should fix the issue.
If you give a property the retain attribute, it will create an accessor method that looks somewhat like this (simplified):
#property (nonatomic, retain) NSValue *value;
- (void)setValue:(NSValue *)aValue {
value = [aValue retain];
}
Only when the retainCount reaches 0 an object is released, using retain, alloc and copy increases the retainCount. Remember: only when using the accessor method the retain actually happens (besides using alloc, retain and copy directly). The accessor method is usually called when using one of the following methods:
// the 2 most obvious ways to call the accessor methods ...
object.value = someValue;
[object setValue:someValue];
You created a retain property in your code, yet you didn't use the accessor method, so the object was never retained.
// no accessor used here ...
managedObjectContext = [(FamQuiz_R0_1AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
If you would release it from this point on, it would cause a crash, since the retainCount would actually become -1 at some point (since it never got to 1 in the first place). Therefore you should set the property like this:
// the dot-notation syntax to make use of the accessor method ...
self.managedObjectContext = [(FamQuiz_R0_1AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
or (in my opinion preferably):
// making use of the accessor method directly, which is very unambiguous ...
NSManagedObjectContext *context = [(FamQuiz_R0_1AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
[self setManagedObjectContext:context];
This way you can be sure the retain actually happens.
The second notation to accessor setters is in my opinion superior and I consider it good habit to use it for setting properties whenever possible. Read more about people who share this opinion and their reasoning on the following sites:
Cocoa Is My Girlfriend
The Big Nerd Ranch

Memory leak problem and i need help #1

I am very new at this and seems to have a leak in this piece of code that i cannot fix:
The Instruments shows on this line with a 100%:
NSMutableArray *read_Question = [[NSMutableArray alloc] initWithCapacity: 0];
I have tried all kind of things but not been able to fix it.
Anyone nice that can advice me how to proceed?
- (NSMutableArray *)readQuestion: (int)questionNr {
NSMutableArray *read_Question = [[NSMutableArray alloc] initWithCapacity: 0];
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:#"idQ"] intValue] == questionNr) {
[read_Question addObject:[info valueForKey:#"question"]];
[read_Question addObject:[info valueForKey:#"qRightAnswer"]];
[read_Question addObject:[info valueForKey:#"qWrongAnswer1"]];
[read_Question addObject:[info valueForKey:#"qWrongAnswer2"]];
}
}
[fetchRequest release];
return [read_Question autorelease];
}
It seams that you are returning the object only inside the if statement. Meaning that if the if statement is false you will not autorelease the array. Or maybe you didn't paste the entire method. Let me know. Instruments is sometimes tricky.
This is a dupe of your other question Memory leak problem and i need help #1
When i did release i got into trouble,
of course. I did try to change the
names on the three and do release so
there was unique names but that did
not work.
Changing the names across three different files? That won't do anything and it indicates that you haven't entirely wrapped your head around objects, pointers, and memory management.
The Objective-C and Memory Management guides will help.
Could this be the reason for the leak
i have in this .m file?
Nope -- as I answered in the other question, the leak is most likely because you retain the object that is returned by that method and then don't release it anywhere.
Instruments is telling you were the leaked object was allocated, not where it was necessarily leaked.
While you may not be autoreleasing the array in all cases on return from that method, you might also be retaining it somewhere else and not balancing that retain with a release.
I am assuming you set the property managedObjectContext to "retain". Change the line to this (include "self" so that it gets retained):
if (self.managedObjectContext == nil) { self.managedObjectContext = [(FamQuiz_R0_1AppDelegate *)
[[UIApplication sharedApplication] delegate] managedObjectContext]; }
Then add your release back in.
Since I believe the code from picciano will fix the issue of the openingsposter, here a small explanation why it should fix the issue.
If you give a property the retain attribute, it will create an accessor method that looks somewhat like this (simplified):
#property (nonatomic, retain) NSValue *value;
- (void)setValue:(NSValue *)aValue {
value = [aValue retain];
}
Only when the retainCount reaches 0 an object is released, using retain, alloc and copy increases the retainCount. Remember: only when using the accessor method the retain actually happens (besides using alloc, retain and copy directly). The accessor method is usually called when using one of the following methods:
// the 2 most obvious ways to call the accessor methods ...
object.value = someValue;
[object setValue:someValue];
You created a retain property in your code, yet you didn't use the accessor method, so the object was never retained.
// no accessor used here ...
managedObjectContext = [(FamQuiz_R0_1AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
If you would release it from this point on, it would cause a crash, since the retainCount would actually become -1 at some point (since it never got to 1 in the first place). Therefore you should set the property like this:
// the dot-notation syntax to make use of the accessor method ...
self.managedObjectContext = [(FamQuiz_R0_1AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
or (in my opinion preferably):
// making use of the accessor method directly, which is very unambiguous ...
NSManagedObjectContext *context = [(FamQuiz_R0_1AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
[self setManagedObjectContext:context];
This way you can be sure the retain actually happens.
The second notation to accessor setters is in my opinion superior and I consider it good habit to use it for setting properties whenever possible. Read more about people who share this opinion and their reasoning on the following sites:
Cocoa Is My Girlfriend
The Big Nerd Ranch

How to load Core Data on another view besides the RootViewController?

I basically have the core data and the app working correctly except for the code in the AppDelegate. The code I'm having problems with is the following:
- (void)applicationDidFinishLaunching:(UIApplication *)application {
RootViewController *tableController = [[RootViewController alloc] initWithStyle:UITableViewStylePlain];
tableController.managedObjectContext = [self managedObjectContext];
self.navigationController = [[UINavigationController alloc] initWithRootViewController:tableController];
[tableController release];
[window addSubview: [self.navigationController view]];
[window makeKeyAndVisible];
}
I don't want to make the managedObjectContext the root view controller upon launch. I'm wanting to make it another view controller. However, if I change the classes to the view controller that I'm needing it for, it loads that view controller upon launch of the app, which is not what I want to do. I still want to launch the root view but I want to be able to load the core data context for my other view controller. I'm really confused on how to fix this issue. I've spent 2 days so far trying to find a way to fix this but no luck yet. Any help would be appreciated.
Also, if I leave out the following in the appdelegate didfinishlaunching:
RootViewController *tableController = [[RootViewController alloc] initWithStyle:UITableViewStylePlain];
tableController.managedObjectContext = [self managedObjectContext];
self.navigationController = [[UINavigationController alloc] initWithRootViewController:tableController];
[tableController release];
I get this error:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '+entityForName: could not locate an NSManagedObjectModel for entity name 'Hello'
EDIT:
Here is the entity code:
- (void)viewDidLoad {
[super viewDidLoad];
self.title = #"Lap Times";
UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(addTime:)];
self.navigationItem.rightBarButtonItem = addButton;
[addButton release];
[self fetchRecords];
}
- (void)addTime:(id)sender {
addTimeEvent *event = (addTimeEvent *)[NSEntityDescription insertNewObjectForEntityForName:#"addTime" inManagedObjectContext:self.managedObjectContext];
[event setTimeStamp: [NSDate date]];
NSError *error;
if (![managedObjectContext save:&error]) {
// This is a serious error saying the record could not be saved.
// Advise the user to restart the application
}
[eventArray insertObject:event atIndex:0];
[self.tableView reloadData];
}
- (void)fetchRecords {
// Define our table/entity to use
NSEntityDescription *entity = [NSEntityDescription entityForName:#"addTime" inManagedObjectContext:self.managedObjectContext];
// Setup the fetch request
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
// Define how we will sort the records
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"timeStamp" ascending:NO];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
[request setSortDescriptors:sortDescriptors];
[sortDescriptor release];
// Fetch the records and handle an error
NSError *error;
NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if (!mutableFetchResults) {
// Handle the error.
// This is a serious error and should advise the user to restart the application
}
// Save our fetched data to an array
[self setEventArray: mutableFetchResults];
[mutableFetchResults release];
[request release];
}
Also if I use my own appdelegate called MyAppDelegate
MyAppDelegate *tableController = [[MyAppDelegate alloc] initWithStyle:UITableViewStylePlain];
tableController.managedObjectContext = [self managedObjectContext];
self.navigationController = [[UINavigationController alloc] initWithRootViewController:tableController];
I get the following error:
Object cannot be set- either readonly property or no setter found
I can't see the problem with the original approach you are taking? You are basically creating the managedObjectContext in your App delegate and passing it on to the tableController via assignment.
The alternative way to go about it is to get your viewController to "ask" for the managedObjectContext from the App delegate. So you'd still have your CoreData methods placed in your AppDelegate and use the following where you want to get a reference to the context. Because the managedObjectContext is lazily loaded on request, it will only get instantiated the first time you access the managedObjectContext method in your app delegate.
AppDelegate *theDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
self.managedObjectContext = theDelegate.managedObjectContext;
PS1: obviously you need to replace AppDelegate with the correct name for your App.
PS2: the reason you're getting the error when you make the changes is that there is no context available for CoreData to work with.
There is nothing magical about the RootViewController beside the name. You can rename it or you can exchange it with any other View Controller as long as these View Controller are configured right. You might want to take the RootViewController and adjust your new View Controller accordingly.
That said I don't understand what you want to do. You might want to post the code that doesn't work.
if you get an exception for +entityForName: you should post the code around +entityForName:.
If I had to make a guess I would say that your code looks like this:
entity = [NSEntityDescription entityForName:#"Hello"
inManagedObjectContext:managedObjectContext];
^^^^^^^^^^^^^^^^^^^^
this means you are using the managedObjectContext without the getter. And the getter uses lazy loading to load the context if it is needed for the first time.
I bet managedObjectContext is nil at this point. Use the debugger to check this out.
And then change the line like this:
entity = [NSEntityDescription entityForName:#"Hello"
inManagedObjectContext:self.managedObjectContext];
^^^^^^^^^^^^^^^^^^^^^^^^^
The code works when you include the four line about the rootviewcontroller because of this call:
tableController.managedObjectContext = [self managedObjectContext];
^^^^^^^^^^^^^^^^^^^^^^^^^^^
this will create the context if it is nil. Lazy loading.
[self managedObjectContext] is the same as self.managedObjectContext
but everything in my post is a guess because you didn't include the code around +entityForName:inManagedObjectContext: