Trying to save NSManagedObject in appDelegate - iphone

i`m having a lot of trouble when i try to save a NSManageObject in the delegate, and worst problems trying to read the objects.
My Delegate is something like this:
#class ViewController;
#class RootViewController;
#interface AppDelegate : UIResponder <UIApplicationDelegate>
{
NSManagedObjectModel *managedObjectModel;
NSManagedObjectContext *managedObjectContext;
NSPersistentStoreCoordinator *persistentStoreCoordinator;
Trips *trip;
Garage *car;
}
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) ViewController *viewController;
#property (strong, nonatomic) RootViewController *rootviewcontroller;
#property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel;
#property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext;
#property (nonatomic, retain, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;
#property (nonatomic, retain) Trips *trip;
#property (nonatomic, retain) Garage *car;
- (NSString *)applicationDocumentsDirectory;
- (void) setCar:(Garage *)Car;
- (void) setTrip:(Trips *)Trip;
- (Trips *) gettrip;
- (Garage *) getcar;
#end
The complete methods to set and get are like this:
- (void) setCar:(Garage *)Car
{
car = Car;
}
- (void) setTrip:(Trips *)Trip
{
trip = Trip;
}
- (Trips *) gettrip
{
return trip;
}
- (Garage *) getcar
{
return car;
}
In the first view when i do:
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
trip = (Trips *)[NSEntityDescription insertNewObjectForEntityForName:#"Trips" inManagedObjectContext:context];
[appDelegate setTrip:trip];
Seems to work OK, even if i do NSLog(#"%#", [appDelegate gettrip]); , it shows me the object without problemas. But when I try to read the same trip with [appDelegate gettrip] in other view, dosen`t work at all, in fact with NSLog it says that the object is the type Garage instead of Trips.
I don`t know what am i doing wrong. Help.

Are you making sure to save the context after you are creating the item? If you do not save it then I do not think that your new entity with be properly saved. In the code you are simply creating the object in the context, but you must save the context for its permanence:
NSError *error = nil;
if(![self.managedObjectContext save:&error]){
//Handle error
}
I hope this helps!

If NSLog is reporting a different object then you need to look at where you are creating a Garage entity.
Perhaps you have a simple copy and paste error?

while you are creating the object to be saved it doesn't look like you are actually saving the object to the persistent storage.
try this:
if (![managedObjectContext save:&error]) {
NSLog(#"ERROR: %#", [error userInfo]);
} else {
NSLog(#"EVERYTHING SHOULD HAVE SAVED PROPERLY");
}
now when you read the managedObject in the other views it should be there.

Is it a memory management problem with your custom setters?
Do they need to be
- (void)setTrip:(Trip *)Trip {
self.trip = Trip;
}
i.e. self.trip = instead of just trip = to use the property - otherwise I don't think it will retain your trip object and it will get autoreleased. Then, when yo come to use it later something else will be in that memory location.
Disclaimer - I'm still not 100% up with ARC yet so I might be wrong. It's worth a try though!

Related

Memory leak vs Zombie - iPhone

My iPhone app is either crashing due to to a zombie, or leaking memory.. I've narrowed it down to 3 lines of code and can reliably get one of the two things to happen by commenting/uncommenting the code. The bugs occur when navigation between a list of results (tableView) and a details page containing a map and a few labels, memory leak happens the first time I navigation from the map back to the list of results, the zombie occurs after maybe 5/6 times navigating to different results and back.
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#define METERS_PER_MILE 1609.344
#interface ResDetailsPageVC : UIViewController <MKMapViewDelegate, UIAlertViewDelegate> {
UISegmentedControl *mapTypeSwitcher;
MKMapView *mapView;
UILabel *nameLabel;
UIButton *addressLabel;
UILabel *telephoneLabel;
NSString *address;
}
#property (nonatomic, retain) IBOutlet UISegmentedControl *mapTypeSwitcher;
#property (nonatomic, retain) IBOutlet MKMapView *mapView;
#property (nonatomic, retain) IBOutlet UILabel *nameLabel;
#property (nonatomic, retain) IBOutlet UIButton *addressLabel;
#property (nonatomic, retain) IBOutlet UILabel *telephoneLabel;
- (IBAction)segmentedControlIndexChanged;
- (IBAction)callButtonClick;
- (IBAction)addressClick;
- (void) callNumber;
#end
#synthesize mapView;
#synthesize mapTypeSwitcher;
#synthesize nameLabel, addressLabel, telephoneLabel;
- (void)dealloc {
// if these lines are commented out - memory leak
// if not - zombie?!
/*self.telephoneLabel = nil;
self.addressLabel = nil;
self.nameLabel = nil;*/
self.mapView = nil;
self.mapTypeSwitcher = nil;
[super dealloc];
}
Somewhere some other piece of code is using the same object whose address is stored in one of those three properties, but that other piece of code has not properly retained the object.
I recommend this to you:
- (void)dealloc {
[telephoneLabel release]; telephoneLabel = nil;
[addressLabel release]; addressLabel = nil;
....
[super dealloc];
}

Change text in UILabel with NSMutablearray data

I'm trying to change the text of a UILabel with text from an array upon a button click, but it doesn't do anything.
#interface Test01AppDelegate : NSObject <UIApplicationDelegate> {
UILabel *helloLabel;
UIButton *hellobutton;
NSMutableArray *madWords;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet UIButton *hellowButton;
#property (nonatomic, retain) IBOutlet UILabel *hellowLabel;
#property (nonatomic, retain) NSMutableArray *madWords;
- (void) madArrays;
- (IBAction)helloYall;
#end
and
#import "Test01AppDelegate.h"
#implementation Test01AppDelegate
#synthesize window = _window;
#synthesize hellowButton;
#synthesize hellowLabel;
#synthesize madWords;
- (void) madArrays {
[madWords addObject:#"Part A"];
[madWords addObject:#"Part B"];
[madWords addObject:#"Part C"];
[madWords addObject:#"Part D"];
}
- (IBAction)helloYall {
[self madArrays];
self.hellowLabel.text = [madWords objectAtIndex:0];
}
I can set the helloLabel text with
#"some text here";
and it works fine. Also, I tried copying the "madArrays" method into the "helloYall" method and it still didn't work. As I said, I can manually set the text and it works, but I'd like to pull the info from an array. Eventually, I'd like to loop through the array to grab the text on each button press, but one step at a time. Thanks.
You never create the madWords array. You need to add:
self.madWords = [NSMutableArray array];
at the top of:
- (void) madArrays {
would probably be a good place. Other possibly good places would be i the class init method or the view controller viewWillAppear method.
// Or you can try this in your init Method:
//first allocate the ivar
- (void)myInitMethod {
madArrays = [[NSMutableArray]alloc]init];
}
//then you can do anything directly to the ivar or throughout de setter
- (void)doAnythingWithiVar {
// do your stuff
}
//when you are done you can dealloc your ivar
- (void)dealloc {
[madArrays release];
[super dealloc];
}
It looks like madArrays is still nil when you come to populate it. At some point you need something like [self setMadArrays:[[NSMutableArray alloc] init]];. Also, don't forget to release madArrays in the dealloc before calling super as you'll have a memory leak.

Using AppDelegate for global readonly data

I have a project on iPhone with iOS4.
A instance variable of app delegate is a dictionary with global readonly data loaded from a plist when app starts.
CalculatorAppDelegate.h
#import <UIKit/UIKit.h>
#class MainViewController;
#interface CalculatorAppDelegate : NSObject <UIApplicationDelegate> {
NSDictionary *RGBSpacesDictionary;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain, readonly) NSDictionary *RGBSpacesDictionary;
#property (nonatomic, retain) IBOutlet MainViewController *mainViewController;
#end
CalculatorAppDelegate.m
#import "CalculatorAppDelegate.h"
#import "MainViewController.h"
#implementation CalculatorAppDelegate
#synthesize mainViewController=_mainViewController;
#synthesize RGBSpacesDictionary;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// load plist
NSString* plistPath1 = [[NSBundle mainBundle] pathForResource:#"RGBSpaces" ofType:#"plist"];
RGBSpacesDictionary = [NSDictionary dictionaryWithContentsOfFile:plistPath1];
etc.
}
Then in MainViewController i am able to successfully reading the dictionary in viewDidLoad
MainViewController.h
#class CalculatorAppDelegate;
#interface MainViewController : UIViewController <FlipsideViewControllerDelegate> {
CalculatorAppDelegate *appDelegate;
}
#property (nonatomic, retain) CalculatorAppDelegate *appDelegate;
etc.
}
MainViewCOntroller.m
#import "CalculatorAppDelegate.h"
#implementation MainViewController
#synthesize appDelegate;
- (void)viewDidLoad
{
[super viewDidLoad];
appDelegate = [[UIApplication sharedApplication] delegate];
RGBSpacesCount = (int) [appDelegate.RGBSpacesDictionary count];
}
In viewDidLoad it is all OK, I can read my dictionary as appDelegate.REGSpaceDictionary.
The problem is with another method of MainVievController called when a button is pressed
- (IBAction) RGBSpaceButtonPressed {
NSLog(#"appDelegate.RGBSpacesDictionary %#", appDelegate.RGBSpacesDictionary);
etc.
}
At this time calling the dictionary (for example with a NSLog) return in a crash.
Can someone help me? Thank you.
In this line
RGBSpacesDictionary = [NSDictionary dictionaryWithContentsOfFile:plistPath1];
you are assigning an autoreleased object straight to the ivar so there is no guarantee how long it will stay around for. You should be assigning a non autoreleased object or going through the setter
// Going through the setter
self.RGBSpacesDictionary = [NSDictionary dictionaryWithContentsOfFile:plistPath1];
// OR
// Non assigning a non autoreleased object
RGBSpacesDictionary = [[NSDictionary alloc] initWithContentsOfFile:plistPath1];
To use the setter you would have to redeclare the property in an extension at the top of the app delegate's .m file like this
#interface CalculatorAppDelegate ()
#property (nonatomic, retain, readwrite) NSDictionary *RGBSpacesDictionary;
#end
...
The rest of your implementation
Try to retain the dictionary in app delegate. It must be deallocated at some point, because you get an autoreleased one and you didn't use the property to set it.
// here you must retain the dictionary
[[NSDictionary dictionaryWithContentsOfFile:plistPath1] retain];
Of course don't forget to release it later in dealloc.

Release of NSManagedObject

I have based a portion of an app on Apple's CoreDataRecipes example code attainable at
http://developer.apple.com/library/ios/#samplecode/iPhoneCoreDataRecipes/Introduction/Intro.html
After some modifications I spent a good few hours tracking down a bug which I must have introduced, but which I solved by removing two lines of code present in apple's code.
I added an author attribute to the NSManagedDataObject recipe, identical in implementation - as far as I could tell - to other string attributes which recipe already had. My new attribute became a zombie after entering and leaving the modal view controlled by IngredientDetailViewController. The dealloc method of IngredientDetailViewController was
- (void)dealloc {
[recipe release];
[ingredient release];
[super dealloc];
}
Having tracked down the bug, I commented out the releases on the recipe and the ingredient (another NSManagedObject) and my app now seems to be functioning. I have now discovered that my code works with or without those release calls; the bug must have been fixed by another change I made. I am now wondering
Why was apple's example code written like this originally?
What was it about the original attributes of the NSManagedObject recipe which meant that they were not susceptible to zombification from the dealloc calls?
If the above hasn't displayed my ignorance enough, I should point out that I am new to Objective C and iPhone development but I would really like to understand what's going on here.
EDITED IN RESPONSE TO COMMENTS AND UPDATED:
I now cannot replicate the zombie creation by uncommenting those lines, obviously another change during bugshooting did the trick. Some of what I originally asked is now invalid but this has left me further confused as to the use of release for NSManagedObjects, since now functionality seems identical with or without those calls. My main question now is just whether or not they should be there. The crash was occuring upon saving in the IngredientDetailView. Here is the header:
#class Recipe, Ingredient, EditingTableViewCell;
#interface IngredientDetailViewController : UITableViewController {
#private
Recipe *recipe;
Ingredient *ingredient;
EditingTableViewCell *editingTableViewCell;
}
#property (nonatomic, retain) Recipe *recipe;
#property (nonatomic, retain) Ingredient *ingredient;
#property (nonatomic, assign) IBOutlet EditingTableViewCell *editingTableViewCell;
#end
and the save method:
- (void)save:(id)sender {
NSManagedObjectContext *context = [recipe managedObjectContext];
/*
If there isn't an ingredient object, create and configure one.
*/
if (!ingredient) {
self.ingredient = [NSEntityDescription insertNewObjectForEntityForName:#"Ingredient"
inManagedObjectContext:context];
[recipe addIngredientsObject:ingredient];
ingredient.displayOrder = [NSNumber numberWithInteger:[recipe.ingredients count]];
}
/*
Update the ingredient from the values in the text fields.
*/
EditingTableViewCell *cell;
cell = (EditingTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
ingredient.name = cell.textField.text;
cell = (EditingTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:0]];
ingredient.amount = cell.textField.text;
/*
Save the managed object 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();
}
[self.navigationController popViewControllerAnimated:YES];
NSLog(#"in ingredient detail save after ingredient pop; - recipe.author is %#", recipe.author);
}
since I'm a new user I can't put the screenshot of the data model here, so here is a link to it: data model screenshot
and finally the Recipe header:
#interface ImageToDataTransformer : NSValueTransformer {
}
#end
#interface Recipe : NSManagedObject {
}
#property (nonatomic, retain) NSString *instructions;
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSString *overview;
#property (nonatomic, retain) NSString *prepTime;
#property (nonatomic, retain) NSSet *ingredients;
#property (nonatomic, retain) UIImage *thumbnailImage;
#property (nonatomic, retain) NSString *author;
#property (nonatomic) BOOL *isDownloaded;
#property (nonatomic) BOOL *isSubmitted;
#property (nonatomic, retain) NSString *uniqueID;
#property (nonatomic) float averageRating;
#property (nonatomic) float numberOfRatings;
#property (nonatomic, retain) NSManagedObject *image;
#property (nonatomic, retain) NSManagedObject *type;
#end
#interface Recipe (CoreDataGeneratedAccessors)
- (void)addIngredientsObject:(NSManagedObject *)value;
- (void)removeIngredientsObject:(NSManagedObject *)value;
- (void)addIngredients:(NSSet *)value;
- (void)removeIngredients:(NSSet *)value;
#end
Thanks again.
Please take a look at Core Data documentation, since Core Data “owns” the life-cycle of managed objects you should not be releasing them at all.
The only time you would release a managed object would be if you had retained it yourself. Seeing as your property definition says that it is retaining the recipe and ingredient objects, when your ingredientviewcontroller is deallocated, it needs to release the recipe and ingredient objects.
When you do something like myIngredientViewController.ingredient = anIngredient, it's like calling a method which would look something like:
- (void)setIngredient:(Ingredient *)ing {
[self willChangeValueForKey:#"ingredient"];
Ingredient *oldIngredient = ingredient;
ingredient = [ing retain];
[oldIngredient release];
[self didChangeValueForKey:#"ingredient"];
}
So in your save method, when it assigns self.ingredient = ..., that is retaining your object yourself - you now have an ownership interest in that object, so you need to release that in your dealloc.
If you think about it in another way, the managed object context has added 1 to the retain count because it has an ownership interest in it, and you have added 1 to the retain count because you want to maintain an ownership interest in it. When you relinquish your ownership interest, by releasing it during dealloc, the retain count goes down 1 and when the managed object context releases it, the retain count would go to zero and it would be deallocated.
That is how normal objects operate, and how you would treat managed objects in most circumstances, but there are a few caveats for managed objects - as the previous poster indicated, the lifecycle of managed objects is controlled by the managed object context, and there are various things that can happen to a managed object that may mean that although the object still exists, it may be deleted in the context, or a fault in the context, or maybe even reused with different data.
You don't usually have to worry about that, but if you use custom managed objects which have their own instance variables that you need to manage the memory for, or other things you want to do when they are created, fetched, turned into faults etc, then you would need to look at the awakeFromInsert, awakeFromFetch, willTurnIntoFault, didTurnIntoFault etc.
But all that is advanced stuff that you won't need until you get into more complex scenarios.
HTH

pushViewController memory leak

I have following code, instrument indicate that the pushViewController method has 32 bytes memory leak on device. Could you please kindly help check what rule I break? Should I change some "retain" to "assign" for declaration? Thanks in advance!
#interface GuideNewsViewController : UIViewController <UITableViewDataSource, UITableViewDelegate> {
#private
NSFetchedResultsController *fetchedResultsController;
NSManagedObjectContext *managedObjectContext;
UITableView *tableView;
NewsListViewController *newsListViewController;
}
#property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;
#property (nonatomic, retain, readonly) NSFetchedResultsController *fetchedResultsController;
#property (nonatomic, retain) UITableView *tableView;
#property (nonatomic, retain, readonly) NewsListViewController *newsListViewController;
#implementation GuideNewsViewController
......
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
Member *member = [fetchedResultsController objectAtIndexPath:indexPath];
self.newsListViewController.managedObjectContext = self.managedObjectContext;
self.newsListViewController.title = member.memberName;
self.newsListViewController.author = member;
**// leak here**
[self.navigationController pushViewController:self.newsListViewController animated:YES];
}
......
#end
#interface NewsListViewController : UIViewController <UITableViewDataSource, UITableViewDelegate> {
#private
NSFetchedResultsController *fetchedResultsController;
NSManagedObjectContext *managedObjectContext;
UITableView *tableView;
Member *author;
}
#property (nonatomic, assign) NSManagedObjectContext *managedObjectContext;
#property (nonatomic, retain) UITableView *tableView;
#property (nonatomic, assign, readonly) NSFetchedResultsController *fetchedResultsController;
#property (nonatomic, assign) Member *author;
#end
Just a guess but, what happen if you change your newsListViewController from retain to assign?
I barely write retain, readonly together.
When does self.newsListViewController be released ? I don't think that point causes problem.
Generally, I pushViewController with following manner.
MySelfViewController *childView = [[MySelfViewController alloc] init];
// set up necessary properties of childView
...
// navigationController will release childView when it pops view controller.
[self pushViewController:childView animated:YES];
// release childView after pushViewController
[childView release];
If childView have to notify self something happened, it can use delegate to notify self.
Edit 1:
An example is as following.
// MySelfViewController.h
#protocol MySelfProtocol <NSObject>
- (void)notifySomethingHappened;
#end
#interface MySelfViewController : UIViewController {
id <MySelfProtocol> _delegate;
}
/// client init childview by pass self as parameter.
/// ex: Inside view controller A, he calls by
/// childView = [[MySelfViewController alloc] initWithDelegate:self];
- (id)initWithDelegate:(id)delegate;
/// other member methods
#end
// MySelfViewController.m
#implement MySelfViewController
- (id)initWithDelegate:(id)delegate
{
if (self = [super init])
{
/// assign policy.
/// childView should not retain parent view or delegate.
/// It is possible to let delegate never run dealloc.
_delegate = delegate;
/// custom initialization
....
}
return self;
}
- (void)someThingHappen
{
[_delegate notifySomethingHappend];
}
#end