Odd Core Data error caused by over releasing? - iphone

Occasional reader and first time question asker, so please be gentle :)
I am creating a Managed Object (Account), that is being passed into a child view controller where its being set in a property that is retained.
Account * account = [[Account alloc] initWithEntity:entity insertIntoManagedObjectContext:context];
AddAccountViewController *childController = [[AddAccountViewController alloc] init];
childController.title = #"Account Details";
childController.anAccount = account;
childController.delegate = self;
[self.navigationController pushViewController:childController animated:YES];
[childController release];
[account release];
The view controller interface:
#interface AddAccountViewController : UIViewController {
}
#property (nonatomic, retain) IBOutlet UITextField * usernameTextField;
#property (nonatomic, retain) IBOutlet UITextField * passwordTextField;
#property (nonatomic, retain) Account * anAccount;
#property (nonatomic, assign) id <AddAccountDelegate> delegate;
- (IBAction)cancel:(id)sender;
- (IBAction)add:(id)sender;
- (IBAction)textFieldDone:(id)sender;
#end
So in code sample 1 I've released the account object because I am no longer interested in it in that method. As it is retained by the AddAccountViewController I have an entry in AddAccountViewController's dealloc that releases it.
However when I go to delete the object from the ManagedObjectContext the app crashes with the following (rather unclear) error:
Detected an attempt to call a symbol in system libraries that is not present on the iPhone:
_Unwind_Resume called from function _PFFaultHandlerLookupRow in image CoreData.
After much debugging & hair pulling I discovered that if I don't release account in AddAccountViewController's dealloc method the app works properly continually and doesn't appear to leak according to Instruments.
Can anyone shed any light as to whats going on? I understand from the docs on properties that those retained need to be released. What have I missed?
Update to answer Kevin's question
The code to delete the object from the ManagedObjectContext is in the RootViewController (that holding the child controller)
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the managed object for the given index path
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
[context deleteObject:[self.fetchedResultsController objectAtIndexPath:indexPath]];
// Save the context.
NSError *error = nil;
if (![context save:&error]) {
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
*/
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
}

Firstly: It sounds like a bug on Apple's part. Core Data is calling _Unwind_Resume, which is (probably) some sort of exception unwind. Exception-unwinding exists on the phone, but (I think) uses the ARM ABI, which uses function names beginning with __cxa_. Are you running on the simulator? Which version of the SDK?
There might be an extra release floating around somewhere which is "balanced" when you remove the call to [account release];.
"Instruments doesn't show any leaks" doesn't mean there aren't any; last I checked it got confused by cycles (i.e. it wouldn't show a leak if you forgot to un-set IBOutlets in dealloc). I tested with NSMutableData * d = [NSMutableData dataWithLength:1<<20]; memcpy(d.mutableBytes, &d, 4);, but an easier test is just [[UIView alloc] initWithFrame:CGRectZero].
If you think it's a retain/release issue, I once debugged these by overriding retain/release/autorelease to call NSLog. I then added breakpoints on all of them, set them to run the command "bt", and clicked the autocontinue. Then run the thing that breaks (in my case I think it was just an extra retain), print out the log output,stick it on a whiteboard, and spend half an hour matching retains and releases.

I had a similar issue ending in a
"Detected an attempt to call a symbol in system libraries that is not present on the iPhone:
_Unwind_Resume called from function _PFFaultHandlerLookupRow in image CoreData."
error message.
My problem was a wrong "cascading" deletion-rule on a relation in the model. With this rule, my top managed object got deleted but still referenced in the code.
After setting the "delete rule" on this relation to "nulify", everything worked as designed.
--> no core data issue...design issue!
Johnny

When ever you delete any managedobject, system will automatically release all reference related to that object. So there is no need to realese object programatically. Once you delete object there you can not access that object in parent class.

Related

how to know managedObjectContext is getting changed in another view controller

I am following the tutorial for core data at here. We have RootViewcontroller and addRecipeViewController.
I list some classes and some functions and a screen for the flow below so that you wont get lost
Recipe.h
#import <CoreData/CoreData.h>
#interface Recipes : NSManagedObject
{
}
#property (nonatomic, retain) NSString * recipeName;
#property (nonatomic, retain) NSString * cookingTime;
#end
addRecipeViewController.h
#class Recipes;
#interface AddRecipeViewController : UIViewController <UITextFieldDelegate> {
Recipes *recipes;
UITextField *textFieldOne;
UITextField *textFieldTwo;
}
addRecipeViewController.m
- (void)save {
1.recipes.recipeName = textFieldOne.text;
2.recipes.cookingTime = textFieldTwo.text;
3.NSError *error = nil;
4.if (![recipes.managedObjectContext save:&error]) {
// Handle error
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
exit(-1); // Fail
}
[self dismissModalViewControllerAnimated:YES];
}
RootViewController.m
- (void)insertNewObject {
AddRecipeViewController *addRecipeView = [[AddRecipeViewController alloc] initWithNibName:#"AddRecipeViewController" bundle:[NSBundle mainBundle]];
Recipes *recipes = (Recipes *)[NSEntityDescription insertNewObjectForEntityForName:#"Recipes" inManagedObjectContext:self.managedObjectContext];
addRecipeView.recipes = recipes;
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController: addRecipeView];
[self.navigationController presentModalViewController:navController animated:YES];
[addRecipeView release];
}
picture for flow :
When event Save of addRecipeViewController is clicked, it will save recipes into managedObjectContext. Sooner or later, the rootViewConroller will retrieve data from managedObjectContext by using NSFetchedResultsController
QUESTION: I dont understand how manageObjectContext is the same for all view controller so that you will get the most updated manageObjectContext at rootViewController after adding or deleting Recipe from manageObjectContext in addRecipeViewController
Please help me to understand this problem.
All comments are welcomed here.
The managedObjectContext is basically your persistence layer and it includes a cache and a way to retrieve objects that are not yet in the cache. You want to avoid having multiple managed object contexts in your app so you don't need to deal with nasty cache synchronization issues.
So I'm not sure what problem you are running into exactly that is causing you to pause, but please don't over complicate the problem. Core Data is nice enough to provide you with a single entry point to the persistence store and keeps everything synchronized for you so you should run with it :)
Also, be sure not to confused NSManagedObjectContext and NSManagedObject. Managed objects live within the context. They are not the same thing.
You probably want to get notification when something changes in your context. If so, read this: Are there Core Data call back methods?

property loses its value (long but easy to understand)

In my app, I have:
car.h
#interface car : NSObject
{
NSString *model;
NSString *price;
// others atributes
}
#property(nonatomic, retain) NSString *model;
#property(nonatomic, retain) NSString *price;
...
myshop.h
#import "car.h"
#interface myshop : UIViewController...
{
car *mycar;
}
#property(nonatomic, retain) car *mycar;
...
myshop.m
...
-(void) viewDidLoad
{
...
mycar = [[car alloc] init];
}
so, I have a method that shows a popover, where I can select a car from a tableview. This popover callback a method in the myshop.m, using delegate, where I assign a value to mycar.model, and call the method doA above, all of this works fine, and shows the value of mycar.model in Output:
-(void) doA
{
NSLog(#"car = %#", mycar.model );
...
}
But... now it is the problem: I have a buttom in the myshop view. When I press this button, the action shows an alert view (there is the delegate in .h). The return of this alert calls:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
[self doA];
...
}
And the program crash when doA tries to write mycar.model to Output. No error is showing in Output View of Xcode. In the code, it shows: Thread 1: Program received signal: EXEC_BAD_ACCESS.
I can show as many cars I want from popover view, but when press the button, and the AlertView closes, the program crashes.
Just for test, I call [self doA] in other methods that runs from another class via delegate and always the app craches.
Any idea, what is wrong?
Completing the code
Is there any error here? (this is a method of car.m)
- (void) setValues: (NSDictionary *) data
{
model = [data objectForKey:#"model"];
price = [data objectForKey:#"price"];
...
I don't initialize the properties (model, price, ...) anywhere in code.
SOLVED !!!
It was a memory management problem!
I change
model = [data objectForKey:#"model"];
by
model = [[NSString alloc] initWithFormat:#"%#", [data objectForKey:#"model"]];
I hope that I am right now! At least the app seens to work well!
Thanks a lot friends !!!
You have got a memory management bug somewhere in your code. I don't know where the bug is, it almost certainly isn't in the code you posted (it all looks perfect to me).
Unless you post more code, we can't help you fix this one.
I strongly recommend you enable ARC. It's fairly new, but nowadays it's old enough everyone should start using it.
There's a very good chance your bug will simply go away if you turn ARC on.
The code you posted looks fine, but somewhere else you must be over-releasing mycar. Profile it with the "zombies" instrument, and it will tell you where it is being released.

NSManagedObjectContext executeFetchRequest returns array containing objects that don't stick around

I have a UITableView I'm populating with data from CoreData. I have a data access class I call a method on to get the array of data to populate the table with. In that method I have something like this:
NSArray *fetchedArray = [context executeFetchRequest:request error:&error];
I was just returning this array directly, but was getting an error in my view controller when I try to set its local property that holds the returned array
#property (nonatomic, retain) NSArray *listData;
and
#synthesize listData; // <-- error here -[CFNumber release]: message sent to deallocated instance...
respectively.
It seemed like the 'retain' in my #property was trying to release the previous listData array, which seemed to have already been released (or, more likely an object in the array or one of its properties had been released).
So in my data access class I added the following after the fetchedArray is produced by the context:
NSMutableArray *listArray = [[[NSMutableArray alloc] init] autorelease];
for (Response *item in fetchedArray) {
[listArray addObject:item];
}
return listArray;
But I still get the same error in the #synthesize listData back in the view controller. It doesn't happen the first time usually, but after tapping through to the detail controller and then going back to the list and then reloading the list with different data (e.g. filtering based on user input which calls the data access method to return an updated list - hence the error in the setter for listData).
I'm not entirely sure if my problem is memory management related or related to something I'm not understanding about what the context returns. It'd be nice if a fetch request returned data that didn't get released when I think I've retained it. :(
EDIT Note that given the answer, the title of the question may be a bit misleading.
Ah - just had to think it through a bit more. My problem was that I was assigning one of the objects in the array to a property on the detail controller, but calling release on that property in my detail controller's dealloc.
After changing #property (nonatomic, assign) to #property (nonatomic, retain) it doesn't crash. Yay. Sooo looking forward to ARC.

iphone development controls null after ViewDidLoad

I am a newbie to both Iphone dev and obj c. I am trying to call a web service when the view is loaded and display a activity indicator during the time it takes to retrieve result from the web service.
My problem is that once the ViewDidLoad is completed, my activityindicator and labels are getting null. Hence after the web service call I am not able to manipulate any of the controls.
I am not able to understand why this is happening; obviously it is my lack of understanding of the basics. I done a bit of homework on this but I have not reached anywhere. It would very helpful if some could please explain why the controls get null once the viewdidload is complete.
Update: thank you for the answers. On viewdidload, I am doing the following, I am animating an activity indicator and assigning some values to my textview. Then I call a web service. Debugging the code, I realized that once viewdidload is complete and -(void) connection: (NSURLConnection *) connection didReceiveResponse:(NSURLResponse *) is called, my text view/activityindicator gets null.
(void)viewDidLoad {
[activityindicator startAnimating];
if(myarray != NULL) {
myuitextview.text = #""; // displaying some values from array
#try{
// call web service
} #catch (NSException *ns) {
}
}
}
Welcome to Objective-C! :) The start can be a bit rough, but once you get used to it, it's great..
It's hard to answer your question without code, cause variables can "turn null" for many reasons. However since you're a newbie, it probably has to do with properties, retaining, etc.
There are obviously many related posts on the subject. Here's one: Objective-C 101 (retain vs assign) NSString
Basically in your .h file you need:
#import <UIKit/UIKit.h>
#interface Car:NSObject{
NSString *name;
}
#property(nonatomic, retain) NSString *name;
And then in your .m file in viewDidLoad:
self.name = [[[NSString alloc] init] autorelease];
// For strings, can use convinient function:
// self.name = [NSString string];
It's very important to use self.name and not just name as the former calls the property setter method and the latter does not (important for retaining the property correctly).
HTH, It's obviously general, but so is your question :)
Oded.

How to share state between viewControllers using the AppDelegate object

I'm trying to share an NSArray object between several different view controllers and my first thought was to add a property on the app delegate as they all have access to this object.
But after some debugging it appears I can't actually share this array for some reason. When I set the object from the first view controller and NSLog the results all is well. But when I attempt to get that array value using another view controller object it always returns UITouchData (not the value previously shown in the logs after my first view controller set the value)
Here is the code that I'm using to set the value
NSArray* cookies = [NSHTTPCookie
cookiesWithResponseHeaderFields:[response allHeaderFields]
forURL:[NSURL URLWithString:#""]];
[appDelegate setAuthCookie:cookies];
Here is part of the .h for my app delegate
#interface SomeAppDelegate : NSObject <UIApplicationDelegate> {
NSArray* authCookie;
}
#property (retain) NSArray* authCookie;
- (void)setAuthCookie:(NSArray *)cookie;
- (NSArray *)getAuthCookie;
#end
Here is the .m for the methods in question
#synthesize authCookie;
- (void)setAuthCookie:(NSArray *)cookie
{
authCookie = cookie;
}
- (NSArray *)getAuthCookie
{
return authCookie;
}
Here is the attempt to grab this array in the second view controller that fails (technically it doesn't fail on this line but I don't get an NSArray back as expected so when I try to use this it fails)
NSArray* cookies = [appDelegate getAuthCookie];
Any way I can share state using the app delegate like this?
Your memory management is wrong, and you are getting a completely different object which has inherited the old array's address when you later use the getter.
Your #property is correct, but you've written your own setter and getter that do not retain the object. You don't need to use both #property/#synthesize and supply your own getter/setter. The former is a newer means of automating the latter.
If you remove your implementations of setAuthCookier: and getAuthCookie then your code should work.