i have this code that passes the core data context to one of the controllers.
it was working great for few days, until the debugger started to give me the "not A type release" error and crashing the app.
i have checked the app for leaks and i found leaks from the SappDelegate object. so i understand that i have to release it but it keeping crashing every time i do it.
any ideas
thanks
shani
SAppDelegate *hbad= [[SAppDelegate alloc] init];
NSManagedObjectContext *context = [hbad managedObjectContext];
[hbad release];
if (!context) {
NSLog(#"problem with mannaged");
}
self.managedObjectContext = context;
If SAppDelegate is your actual app delegate, that is not the correct way to get it. you should change your code to:
SAppDelegate *hbad= [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [hbad managedObjectContext];
if (!context) {
NSLog(#"problem with mannaged");
}
self.managedObjectContext = context;
Also, leaks will not cause crashes until MUCH later when your application runs out of memory and the system kills it.
I'm not entirely sure why you're creating a new SAppDelegate. You should only have one of these and it gets created for you on startup. Why do you need another SAppDelegate instance?
You should see Elfred's answer to get the app delegate instead of creating one.
However, there is one bug in the code you have posted . . .
You need to retain the context until you're done with it. Either :
SAppDelegate *hbad= [[SAppDelegate alloc] init];
NSManagedObjectContext *context = [[hbad managedObjectContext] retain];
[hbad release];
if (!context) {
NSLog(#"problem with mannaged");
}
self.managedObjectContext = context;
[context release];
or release hbad later on :
SAppDelegate *hbad= [[SAppDelegate alloc] init];
NSManagedObjectContext *context = [hbad managedObjectContext];
if (!context) {
NSLog(#"problem with mannaged");
}
self.managedObjectContext = context;
[hbad release];
Related
I have my coredata working fine, but when I put a setValue inside an if, it doesn't work,
this is the code>
-(void) salvalo {
StaffManagerAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSManagedObject *newDay;
newDay = [NSEntityDescription insertNewObjectForEntityForName:#"DaysAttendance"
inManagedObjectContext:context];
[newDay setValue:startday forKey:#"start"];
[newDay setValue:today forKey:#"datef"];
[newDay setValue:myNumber forKey:#"empID"];
[newDay setValue:myNumber2 forKey:#"intID"];
//[newDay setValue:internet forKey:#"sinch"];
NSError *error;
[context save:&error];
if ([internet isEqualToString:#"YES"] ) {
[self insertJson];
[newDay setValue:internet forKey:#"sinch"];
}
else {
[newDay setValue:#"NO" forKey:#"sinch"];
}
//[self.view removeFromSuperview];
[self dismissModalViewControllerAnimated:YES];
}
please note that when I use the code inside the if in the commented part
//[newDay setValue:internet forKey:#"sinch"];
, it saves normally that value (YES/NO) to the coredata, but inside the if, it doesnt work, even if I put al the delegate and context stuff inside the if,,
so what is the problem? how to fix it?
thanks a lot!
The line [context save:&error]; is before the if statements.
You may be setting the new value properly, just not saving. The problem might just be this oversight.
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
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
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:
In core data for the iPhone, I was getting all sorts of errors trying to save data to a NSManagedObjectContext.
I believe that my issues were all to do with me using a NSManagedObjectContext that was being used in multiple threads.
So I wanted to create a new NSManagedObjectContext and try that, but I cannot find the example code to simply create a new instance...
I know its simple, but I would really appreciate any help here.
Note, I have seen this article on the Apple docs: http://developer.apple.com/iphone/library/documentation/cocoa/conceptual/CoreDataUtilityTutorial/Articles/05_createStack.html
But this uses some code that I am not familiar with, like the XMLStore which is not supported on the iPhone, etc.
this is the code to create a new context:
- (NSManagedObjectContext *)managedObjectContext {
NSManagedObjectContext *managedObjectContext = nil;
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
managedObjectContext = [[NSManagedObjectContext alloc] init];
[managedObjectContext setPersistentStoreCoordinator:coordinator];
[managedObjectContext setUndoManager:nil];
}
return [managedObjectContext autorelease];
}
It's simply create a new instance of the context and set the store that you would like to use.
If you have multiple stores, you would go for something like that:
- (NSManagedObjectContext *)managedObjectContextForStore:(NSString *)store {
NSManagedObjectContext *managedObjectContext = nil;
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinatorForStore:store];
if (coordinator != nil) {
managedObjectContext = [[NSManagedObjectContext alloc] init];
[managedObjectContext setPersistentStoreCoordinator:coordinator];
[managedObjectContext setUndoManager:nil];
}
return [managedObjectContext autorelease];
}
For more info, please have a look at Apple's Core Data Tutorial for iOS.
Cheers!