正确创建新对象的方式,这些对象是在应用程序委托中定义的NSDictionary和NSArray对象的副本 [英] Proper way of creating new objects which are copies of NSDictionary and NSArray objects defined in app delegate

查看:159
本文介绍了正确创建新对象的方式,这些对象是在应用程序委托中定义的NSDictionary和NSArray对象的副本的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  ... 

@interface DataParser:NSObject {
//保存从XML文件获取的数据的数据对象
NSMutableDictionary * personnel;
NSMutableDictionary * schedule;
NSMutableDictionary *今天;
}

@property(nonatomic,retain)NSMutableDictionary *人员;
@property(nonatomic,retain)NSMutableDictionary * schedule;
@property(nonatomic,retain)NSMutableDictionary *今天;

...

现在在这些字典中我存储(可变)字典以及使用解析的XML数据保存NSString对象的数组。因为我不想修改这些保存已解析数据的原始对象(也就是说,我只想在登录阶段修改它们,而不是在任何一个表视图控制器中),我正在创建一个新的字典对象每个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错误。要做一个很长的故事:什么是最好的方式去做单独对象或应用程序委托中定义的对象的独立副本,哪些可以根据请求动态更新?既然我已经进入这个项目,很多事情都在进行中,我意识到我的问题可能有点模糊。尽管如此,我希望有人能够以某种方式启发我。

解决方案

深层副本通常是递归的。一种方法是将 -deepCopy 方法添加到NSDictionary和NSArray。字典版本可能如下所示:

   - (NSDictionary *)deepCopy 
{
NSMutableDictionary * temp = [self mutableCopy];
for(id key in temp){
id item = [temp objectForKey:key];
if([item respondToSelector:@sel(deepCopy)] {
//处理深度可复制的项目,即字典和数组
[temp setObject:[item deepCopy] forKey:key]
}
else if([item respondToSelector:@(copy)]){
//大多数数据对象实现NSCopyable,所以将在这里处理
[temp setObject:[item copy ] forKey:key];
}
else {
//在这里处理不可复制的项目,可能会抛出一个异常
}
}
NSDictionary * newDict = [[temp copy] autorelease];
[temp release]
return newDict;
}

我没有测试过,所以要小心点,你会想做类似于NSArray的东西。



请注意视图不可复制。


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 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.

这篇关于正确创建新对象的方式,这些对象是在应用程序委托中定义的NSDictionary和NSArray对象的副本的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆