Правильный способ создания новых объектов, которые являются копиями объектов NSDictionary и NSArray, определенных в делегате приложения - PullRequest
0 голосов
/ 25 августа 2011

Мне интересно, как правильно сделать копию объекта, определенного в делегате приложения, или одноэлементного объекта. Короче говоря, я делаю приложение, которое требует пользователя для входа в систему. Это представление входа в систему является просто модальным контроллером представления поверх «реального» приложения, которое состоит из tabbarcontroller и некоторых контроллеров табличного представления. После успешного входа в систему происходит отправка запроса данных на удаленный сервер, и контроллер модального представления отклоняется, показывая контроллер панели вкладок и представления таблицы, содержащие данные 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. Поскольку я не хочу изменять эти исходные объекты, содержащие проанализированные данные (то есть я хочу изменить их только на этапе входа в систему, но не в любом из контроллеров таблиц), я создаю новый объект словаря, который содержит копия содержимого одного из словарей выше в каждом контроллере таблицы. Так, например, в loadView контроллера представления с именем ScheduleViewController у меня есть

...

@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, и словари в объекте singleton обновляются (новыми) данными (я проверяю, содержат ли они какие-либо данные, если так, то я вызываю removeAllObjects перед тем, как снова заполняя их вновь проанализированными данными). На этом этапе словари во всех контроллерах представления также должны быть обновлены, однако я не совсем уверен, как поступить правильно. Я заметил, что loadView не всегда вызывается снова в этом случае, и поэтому для этого я добавил тот же код, что и выше, в loadView для каждого метода viewWillAppear. После перехода назад и вперед между различными представлениями или перехода назад и вперед между дочерними представлениями представления таблицы, однако, я получаю ошибку EXC_BAD_ACCESS. Я подозреваю, что это связано с неправильным сохранением копий оригинальных словарей, но, похоже, я не могу найти решение этой проблемы. Вместо использования dictionaryWithDictionary, который, как я подозреваю, является неправильным, я также попробовал другой подход, где вместо использования объектов типа NSDictionary в ScheduleViewController я использую 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 ]

2 голосов
/ 25 августа 2011

Глубокие копии часто делаются рекурсивно.Один из способов сделать это - добавить методы -deepCopy в NSDictionary и NSArray.Версия словаря может выглядеть так:

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

Обратите внимание, что представления не могут быть скопированы.

0 голосов
/ 13 марта 2014

Это довольно типичный шаблон, когда вы создаете массив или словарь с некоторым кодом, поэтому очевидно, что он должен быть изменяемым, когда вы добавляете в него биты, и когда вы закончите, вы не захотите, чтобы он когда-либо изменился. Для этого:

Имейте свойство как

@property (...) NSArray* myArray;

Когда вы вычисляете содержимое myArray, используйте изменяемый массив для его построения, например

NSMutableArray* myMutableArray = [NSMutableArray array];

Когда вы закончите сборку массива, просто используйте

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