创建新对象的正确方法,这些对象是在app delegate中定义的NSDictionary和NSArray对象的副本

时间:2011-08-25 15:08:28

标签: ios delegates singleton nsdictionary exc-bad-access

我想知道正确的方法是制作app delegate或singleton对象中定义的对象的副本。简而言之,我正在制作一个需要用户登录的应用程序。这个登录视图只是“真实”应用程序顶部的模态视图控制器,它由一个tabbarcontroller和一些tableview控制器组成。成功登录后,会向远程服务器发送数据请求,并取消模态视图控制器,显示标签栏控制器和保存XML数据的表视图。为了解析传入的数据,我创建了一个名为DataParser的单例对象,它具有接口

...

@interface DataParser : NSObject {
    // Data objects that hold the data obtained from XML files
    NSMutableDictionary *personnel;       
    NSMutableDictionary *schedule;          
    NSMutableDictionary *today;
}

@property (nonatomic, retain) NSMutableDictionary *personnel;
@property (nonatomic, retain) NSMutableDictionary *schedule;
@property (nonatomic, retain) NSMutableDictionary *today;

...

现在在这些词典中,我存储(可变的)字典和数组,其中包含NSString对象和解析的XML数据。因为我不想修改这些包含解析数据的原始对象(也就是说,我只想在登录阶段修改它们,而不是在任何tableview控制器中修改它们),我正在创建一个新的字典对象,它保存每个tableview控制器中上面一个词典的内容的副本。例如,在一个名为ScheduleViewController的视图控制器的loadView中,我有

...

@interface ScheduleViewController : UITableViewController {

    NSDictionary *copyOfSchedule;
}

@property (nonatomic, retain) NSDictionary *copyOfSchedule;

...

@end


@implementation ScheduleViewController

@synthesize copyOfSchedule;

- (void)loadView {
    [super loadView];

    DataParser *sharedSingleton = [DataParser sharedInstance];
    self.copyOfSchedule = [NSDictionary dictionaryWithDictionary:sharedSingleton.schedule];
}

...

现在这似乎工作正常。然而,当用户“注销”时,唯一的困难就出现了,这需要将登录模式视图控制器弹回到堆栈上。当用户再次按下登录按钮时,会向服务器发送一个新的XML数据请求,并使用(新)数据刷新单个对象中的字典(我检查它们是否包含任何数据,如果是,我之前调用removeAllObjects)用新解析的数据再次填充它们)。此时,所有视图控制器中的字典也应该更新,但是我不太确定如何以正确的方式进行此操作。我注意到在这种情况下并不总是再次调用loadView,所以为此我在loadView中为每个viewWillAppear方法添加了与上面相同的代码。在不同视图之间来回导航或在桌面视图的子视图之间来回导航几次后,我收到了EXC_BAD_ACCESS错误。我怀疑这与未正确保留原始词典的副本有关,但我似乎无法找到解决方案。我还怀疑不是使用且怀疑不是正确方法的dictionaryWithDictionary,而是尝试了一种不同的方法,而不是在ScheduleViewController中使用NSDictionary类型的对象,而是使用NSMutableDictionary。所以:

...

@interface ScheduleViewController : UITableViewController {
    NSMutableDictionary *copyOfSchedule;
}

@property (nonatomic, retain) NSMutableDictionary *copyOfSchedule;

...

@end


@implementation ScheduleViewController

@synthesize copyOfSchedule;

- (void)loadView {
    [super loadView];

    DataParser *sharedSingleton = [DataParser sharedInstance];
    self.copyOfSchedule = [[NSMutableDictionary alloc] initWithDictionary:sharedSingleton.schedule];
}

- (void)viewWillAppear {
    DataParser *sharedSingleton = [DataParser sharedInstance];
    [self.copyOfSchedule removeAllObjects];
    [self.copyOfSchedule addEntriesFromDictionary:sharedSingleton.schedule];

    [self.tableView reloadData];
}

...

但这并没有摆脱EXC_BAD_ACCESS错误。简而言之:在单个对象或应用程序委托中定义的对象的独立副本以及可以根据请求动态更新的最佳方法是什么?由于我已经非常喜欢这个项目并且正在进行中,我意识到我的问题可能有点模糊。尽管如此,我希望有人能以某种方式启发我。

2 个答案:

答案 0 :(得分:2)

深层副本通常是递归的。一种方法是向NSDictionary和NSArray添加-deepCopy方法。字典版本可能是这样的:

- (NSDictionary*)deepCopy
{
    NSMutableDictionary *temp = [self mutableCopy];
    for (id key in temp) {
        id item = [temp objectForKey:key];
        if ([item respondsToSelector:@sel(deepCopy)] {
            // handle deep-copyable items, i.e. dictionaries and arrays
            [temp setObject:[item deepCopy] forKey:key]
        }
        else if ([item respondsToSelector:@(copy)]) {
            // most data objects implement NSCopyable, so will be handled here
            [temp setObject:[item copy] forKey:key];
        }
        else {
            // handle un-copyable items here, maybe throw an exception
        }
    }
    NSDictionary *newDict = [[temp copy] autorelease];
    [temp release]
    return newDict;
}

我没有测试过,所以要小心一点。你想为NSArray做类似的事情。

请注意,视图不可复制。

答案 1 :(得分:0)

使用某些代码构建数组或字典是一种非常典型的模式,所以当你向它添加位时,它必须是可变的,当你完成时,你不希望它永远改变。要做到这一点:

拥有像

这样的属性
@property (...) NSArray* myArray;

当你计算myArray的内容时,使用一个可变数组来构建它,比如

NSMutableArray* myMutableArray = [NSMutableArray array];

完成数组构建后,只需使用

即可
self.myArray = [NSArray arrayWithArry:myMutableArray];