Singleton, NSMutableDictionary and plist
I have a singleton called SettingsManager in my app that should take care of reading/writing settings to a plist. Singleton is synthesized using macro from Cocoa With Love (zipped macro file). I have only changed retain's return type to (oneway void) from (void). This is due to new compiler in iOS that can take care of memory management. My problem is that whenever i call savePrefs, button calling the selector "hangs" on active state, and sometimes I am getting EXC_BAD_ACCESS. So I guess I failed with memory management. Surprisingly, when no plist file is available (right after app installation), all the defaults are correctly stored and retrieved from my NSMutableDictionary. Below is my code. Will be super grateful for your help.
Martin
CODE:
1/ Singleton header
#import <Foundation/Foundation.h>
#define SALARY_PREF_KEY @"MonthlySalary"
#define DEFAULT_SALARY [NSNumber numberWithDouble: 0.0]
#define CURRENCY_PREF_KEY @"Curren开发者_StackOverflow社区cy"
#define DEFAULT_CURRENCY @"USD"
#define EXPENSES_PREF_KEY @"Expenses"
#define DEFAULT_EXPENSES [NSNumber numberWithDouble: 0.0]
#define TIME_INTERVAL_PREF_KEY @"TimeInterval"
#define DEFAULT_INTERVAL [NSNumber numberWithInteger: 2]
#define SAVINGS_PREF_KEY @"Savings"
#define DEFAULT_SAVINGS [NSNumber numberWithDouble: 0.0]
@interface SettingsManager : NSObject {
NSString *prefsFilePath;
NSMutableDictionary *settingsDictionary;
NSNumber *salary;
NSString *currency;
NSNumber *expenses;
NSNumber *timeInterval;
NSNumber *savings;
}
+ (SettingsManager *)sharedSettingsManager;
@property (nonatomic, retain) NSString *prefsFilePath;
@property (nonatomic, retain) NSMutableDictionary *settingsDictionary;
@property (nonatomic, retain) NSNumber *salary;
@property (nonatomic, retain) NSString *currency;
@property (nonatomic, retain) NSNumber *expenses;
@property (nonatomic, retain) NSNumber *timeInterval;
@property (nonatomic, retain) NSNumber *savings;
- (void) savePrefs;
@end
2/ Implementation:
#import "SynthesizeSingleton.h"
#import "SettingsManager.h"
@implementation SettingsManager
SYNTHESIZE_SINGLETON_FOR_CLASS(SettingsManager);
@synthesize settingsDictionary,
salary,
currency,
expenses,
timeInterval,
savings,
prefsFilePath;
- (id)init {
self = [super init];
if (self) {
if (prefsFilePath == nil) {
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex: 0];
prefsFilePath = [documentsDirectory stringByAppendingPathComponent:@"iEarn.plist"];
}
if ([[NSFileManager defaultManager] fileExistsAtPath: prefsFilePath]) {
settingsDictionary = [[NSMutableDictionary alloc]
initWithContentsOfFile: prefsFilePath];
}
else {
settingsDictionary = [[NSMutableDictionary alloc] initWithCapacity: 7];
[settingsDictionary setObject: DEFAULT_SALARY forKey: SALARY_PREF_KEY];
[settingsDictionary setObject: DEFAULT_CURRENCY forKey: CURRENCY_PREF_KEY];
[settingsDictionary setObject: DEFAULT_EXPENSES forKey: EXPENSES_PREF_KEY];
[settingsDictionary setObject: DEFAULT_INTERVAL forKey: TIME_INTERVAL_PREF_KEY];
[settingsDictionary setObject: DEFAULT_SAVINGS forKey: SAVINGS_PREF_KEY];
[settingsDictionary writeToFile: prefsFilePath atomically: YES];
}
self.salary = [settingsDictionary objectForKey:SALARY_PREF_KEY];
self.currency = [settingsDictionary objectForKey:CURRENCY_PREF_KEY];
self.expenses = [settingsDictionary objectForKey:EXPENSES_PREF_KEY];
self.timeInterval = [settingsDictionary objectForKey:TIME_INTERVAL_PREF_KEY];
self.savings = [settingsDictionary objectForKey:SAVINGS_PREF_KEY];
}
return self;
}
- (void) savePrefs {
[settingsDictionary setObject: salary forKey: SALARY_PREF_KEY];
[settingsDictionary setObject: currency forKey: CURRENCY_PREF_KEY];
[settingsDictionary setObject: expenses forKey: EXPENSES_PREF_KEY];
[settingsDictionary setObject: timeInterval forKey: TIME_INTERVAL_PREF_KEY];
[settingsDictionary setObject: savings forKey: SAVINGS_PREF_KEY];
[settingsDictionary writeToFile: prefsFilePath atomically: YES];
}
- (void) dealloc {
[settingsDictionary dealloc];
[super dealloc];
}
@end
3/ How I call savePrefs
- (IBAction)saveButtonPressed:(id)sender {
[[SettingsManager sharedSettingsManager] setSalary: [NSNumber numberWithDouble: [salary.text doubleValue]]];
[[SettingsManager sharedSettingsManager] setCurrency: currency.text];
[[SettingsManager sharedSettingsManager] setExpenses: [NSNumber numberWithDouble: [expenses.text doubleValue]]];
[[SettingsManager sharedSettingsManager] setTimeInterval: [NSNumber numberWithInt: [intervalStepper value]]];
[[SettingsManager sharedSettingsManager] setSavings: [NSNumber numberWithDouble: [savings.text doubleValue]]];
[[SettingsManager sharedSettingsManager] savePrefs];
}
It seems that you access directly the prefsFilePath
instance variable instead of using its accessor:
prefsFilePath = [documentsDirectory stringByAppendingPathComponent:@"iEarn.plist"];
The value stored is auto-released, so after the current pool is drained, the reference is no longer valid. Instead, you should use:
self.prefsFilePath = [documentsDirectory stringByAppendingPathComponent:@"iEarn.plist"];
or
prefsFilePath = [[documentsDirectory stringByAppendingPathComponent:@"iEarn.plist"] retain];
Note: You may either prefix all the property accesses to avoid problems, or rename the instance variables.
精彩评论