Proper way of creating new objects which are copies of NSDictionary and NSArray objects defined in app delegate
I am wondering what the correct way is to make a copy of an object defined in the app delegate or a singleton object. In short, I am making an app which requires a user to login. This login view is just a modal view controller on top of the 'real' app, which consists of a tabbarcontroller, plus some tableview controllers. After a successful login, there is send a data request to a remote server, and the modal view controller is dismissed, revealing the tabbar controller and table views holding the XML data. To parse the incoming data, I have created a singleton object named DataParser, which has interface
...
@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;
...
Now in these dictionaries I store (mutable) dictionaries and arrays holding NSString objects with the parsed XML data. Since I do not want to modify these original objects holding the parsed data (that is to say, I only want to modify them at the login stage, but not in any of the tableview controllers), I am creating a new dictionary object which holds a copy of the content of one of the dictionaries above in each tableview controller. So for instance, in the loadView of a view controller called ScheduleViewController I have
...
@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];
}
...
Now this seems to work fine. The only difficulty arises however, when the user 'logs out', which entails popping the login modal view controller back on the stack. When the user presses the login button again, then a new XML data request is send to the server and the dictionaries in the singleton object get refreshed with the (new) data (I check if they contain any data, if so I call removeAllObjects before filling them up again with newly parsed data). At this point the dictionaries in all view controllers should be updated too, however I am not quite sure how to go about this the right way. I have noticed that loadView is not always called again in this case and so to this end I have added the same code as above in loadView to every viewWillAppear method. After navigating back and forth between the different views or navigating back and forth between child views of a tableview a couple of times, I receive an EXC_BAD_ACCESS error however. I suspect this has to do with not properly retaining the copies of the original dictionaries, but I don't seem to be able to find a solution around this. Instead of using dictionaryWithDictionary, which I suspect is not the right way to go anyway, I also tried a different approach, where instead of using objects of type NSDictionary in ScheduleViewController I use NSMutableDictionary. So:
...
@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];
}
...
But this doesn't get rid of the EXC_BAD_ACCESS errors. To make a very long story short: what would be the开发者_Go百科 best way to go about making independent copies of objects defined in a singleton object or app delegate and which can be dynamically updated at request? Since I am already rather into the project and lots is going on, I realize that my question may be a bit vague. Nonetheless I hope there is somebody who could enlighten me somehow.
Deep copies are often made recursively. One way to do it would be to add -deepCopy
methods to NSDictionary and NSArray. The dictionary version might go like this:
- (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;
}
I haven't tested that, so be a little careful. You'll want to do something similar for NSArray.
Note that views are not copyable.
It is quite a typical pattern that you build an array or dictionary with some code, so clearly it must be mutable while you add bits to it, and when you're done you don't want it ever to change. To do this:
Have a property like
@property (...) NSArray* myArray;
When you calculate the contents of myArray, use a mutable array to build it, like
NSMutableArray* myMutableArray = [NSMutableArray array];
When you're done building the array, just use
self.myArray = [NSArray arrayWithArry:myMutableArray];
精彩评论