So I have created a simple UITableViewController that I push onto the NavigationController.
I created an initWithPList method that reads a plist and stores the values into an NSMutableDictionary in the items @property in a class called Settings.
The NSMutableDictionary is populated correctly in the initWithStyle method. But in viewDidLoad, the NSMutableDictionary items @property of the Settings NSObject is nil.
I am not sure what I am doing wrong any ideas????
Here is some code:
Showing the ViewController:
TestViewController *settingsViewController = [[TestViewController alloc] initWithStyle:UITableViewStyleGrouped];
[[self navigationController] pushViewController:settingsViewController animated:YES]
Initializing the TestViewController:
- (id)initWithStyle:(UITableViewStyle)style{
if (self = [super initWithStyle:style]) {
// Init settings property here
settings = [[Settings alloc] initWithPList];
// settings.items has a count of 5 here....which is correct
}
return self;
}
viewDidLoad for the TestViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Check settings object here to see if it still has items
// settings.items is nil here for somereason??
NSLog(@"%i", [settings.items count]); // now it's nil here?
}
The Settings NSObject:
#import <Foundation/Foundation.h>
@interface Settings : NSObject {
NSMutableDictionary *items;
NSString *path;
}
@property(nonatomic, retain) NSMutableDictionary *items;
@property(nonatomic, retain) NSString *path;
-(id)initWithPList;
@end
A snippet of the @implementation:
@synthesize items;
@synthesize path;
-(id) initWithPList {
path = [[self documentsDirectory]stringByAppendingPathComponent:@"Settings.plist"];
if(![[NSFileManager defaultManager]fileExistsAtPath:path])
{
[[NSFileManager defaultManager]copyItemAtPath:[[NSBundle mainBundle]pathForResource:@"Settings" ofType:@"plist"] toPath:path error:nil];
}
items = [NSMutableDictionary dictionaryWithContentsOfFile:path];
return self;
}
By synthesizing this way, the backing ivar for the property is named
items. So when you sayyou’re assigning directly to the ivar. In pre-ARC manual memory management, any direct manipulation of the ivar has no connection with “retain” property. Retain/release at the ivar level is totally up to you.
Now because you’re creating a dictionary by calling
+dictionaryWithContentsOfFile:, the dictionary is autoreleased. So you’ve given a persistent variable (theitemsivar) a transient object. Poof, it’s gone!The solution is to take ownership of the dictionary. Some convenience methods don’t have a non-autorelease equivalent, so we’d have to add a
retainto them. But in this case, there is an equivalent initializer:You have ownership. Problem solved. (This also avoids adding anything to the autorelease pool.)
Extra:
It’s still confusingly dangerous for the ivar have the same name as the property. It’s easy to get lulled into thinking that you’re getting the attributes of the property whenever you see
items. First, let’s get rid of the explicit declaration of ivars. They’re not needed for properties.Apple’s convention is to prefix ivars with an underscore. If you’re using Xcode 4.5, simply omit the
@synthesizestatement entirely. For earlier Xcode, change the synthesize to use a slightly different name for the ivar:Then
initWithPlistcan sayThe underscore serves as a reminder: “This is the ivar! Memory management is your problem!” (And so, you dutifully
releaseit in thedeallocmethod.)