单例、NSMutableDictionary 和 plist

Posted

技术标签:

【中文标题】单例、NSMutableDictionary 和 plist【英文标题】:Singleton, NSMutableDictionary and plist 【发布时间】:2011-08-24 12:27:16 【问题描述】:

我的应用中有一个名为 SettingsManager 的单例,它应该负责对 plist 的读/写设置。 Singleton 是使用来自 Cocoa With Love (zipped macro file) 的宏合成的。我只是将保留的返回类型从 (void) 更改为 (oneway void)。这是由于 ios 中的新编译器可以处理内存管理。我的问题是,每当我调用 savePrefs 时,调用选择器的按钮都会在活动状态下“挂起”,有时我会收到 EXC_BAD_ACCESS。所以我想我在内存管理方面失败了。令人惊讶的是,当没有可用的 plist 文件时(在应用程序安装后立即),所有默认值都会正确存储并从我的 NSMutableDictionary 中检索。下面是我的代码。将非常感谢您的帮助。

马丁

代码:

1/ 单例标题

#import <Foundation/Foundation.h>

#define SALARY_PREF_KEY @"MonthlySalary"
#define DEFAULT_SALARY [NSNumber numberWithDouble: 0.0]
#define CURRENCY_PREF_KEY @"Currency"
#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/ 实施:

#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/ 我怎么称呼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];

【问题讨论】:

【参考方案1】:

您似乎直接访问了prefsFilePath 实例变量,而不是使用它的访问器:

prefsFilePath = [documentsDirectory stringByAppendingPathComponent:@"iEarn.plist"];

存储的值是自动释放的,所以在当前池耗尽后,引用不再有效。相反,您应该使用:

self.prefsFilePath = [documentsDirectory stringByAppendingPathComponent:@"iEarn.plist"];

prefsFilePath = [[documentsDirectory stringByAppendingPathComponent:@"iEarn.plist"] retain];

注意:您可以为所有属性访问添加前缀以避免出现问题,或者rename the instance variables。

【讨论】:

非常感谢!它挽救了这个问题。我实际上是在代码中到处寻找错误,但在 prefsFilePath 分配中......由于我来自 Java,仍然需要学习很多关于内存管理的知识。但是与强制 GC 运行相比,拥有更低级别的控制感觉是正确的。再次感谢!

以上是关于单例、NSMutableDictionary 和 plist的主要内容,如果未能解决你的问题,请参考以下文章

读取和写入 NSMutableDictionary 到 plist 文件

NSDictionary 和 NSMutableDictionary 组合

过滤/搜索 NSMutableDictionary

iOS NSMutableDictionary中UIImage的存储和读取

从一个类访问和更改 NSMutableDictionary 的值到另一个 iOS

c# 中 NSMutableDictionary()、NSMutableArray()、NSArray 和 NSDictionary 的等价物是啥?