Как вы сохраняете объект класса, используемый другим классом в Objective-C?

Я делаю приложение Cookbook для iPad и iPod, и у меня есть массив моего класса Recipe в моем классе Cookbook.

@interface Cookbook : NSObject<NSCoding>{
NSMutableArray* recipes;
}

Это в моем классе Cookbook, и в моем классе рецептов у меня есть это:

@interface Recipe : NSObject<NSCoding>{
NSString* name;
NSMutableArray* ingredients; //List of ingredients for the recipe
UIImage* recipePicture;
NSMutableArray* instructions;
unsigned int prepTime;//in seconds
NSDate* dateAdded;
}

(На самом деле у меня здесь больше переменных, но я не хотел заливать их чрезмерным количеством)

Моя проблема в основном в функции сохранения/загрузки. Я уже задавал аналогичный вопрос здесь: Как я могу сохранить объект Objective-C, который не является объектом списка свойств, или есть ли лучший способ для этого, чем список свойств?

Это заставило меня решить, что лучше всего использовать NSCoding, и я уже реализовал метод и для него в соответствии с тем, как было предложено с NSCoding.

Моя основная проблема заключается в том, что я не могу сохранить и успешно получить рецепты.

У меня также были проблемы с получением каталога в моем файле RecipeList.plist для хранения рецептов.

Любая помощь будет принята с благодарностью, так как это было причиной, по которой я не могу продолжать делать это приложение.

В моем Cookbook.m у меня есть:

-(id) initWithCoder:(NSCoder *)aDecoder{
NSLog(@"Init With Coder - Cookbook");
if(self = [super init]){
    recipes = [aDecoder decodeObjectForKey:@"recipes"];
}
return self;
}

В моем Recipe.m:

-(id) initWithCoder:(NSCoder *)aDecoder{
if(self = [super init]){
    name = [aDecoder decodeObjectForKey:@"name"];
    ingredients = [aDecoder decodeObjectForKey:@"ingreds"];
    recipePicture = [aDecoder decodeObjectForKey:@"recipePict"];
}
return self;
}

Еще раз, у меня есть больше переменных, это просто для простоты. Кроме того, это моя попытка получить путь к файлу RecipeList.plist:

+(NSString*) filePath{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,     NSUserDomainMask, YES);
NSString *path = [paths objectAtIndex:0];
path = [path stringByAppendingPathComponent:@"RecipeList.plist"];

NSLog(@"%@",path);
return path;
}

Моя попытка сохранить метод в моем AppDelegate.m:

-(void) save:(NSString *)path cookbook:(Cookbook *)cookbook{
BOOL b = [[NSFileManager defaultManager] fileExistsAtPath:path];
NSLog(@"File exists: %i",b); //1 = exists, 0 = doesn't

NSMutableData* data = [[NSMutableData alloc] init];
if(data){ //If the data object was successfully initialized
    NSKeyedArchiver* archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
    if(archiver){
        //Encode the recipe using the coder method defined in recipe.
        [archiver encodeInt:1 forKey:@"Version"];
        [archiver encodeObject:cookbook forKey:@"Cookbook"];
        [archiver finishEncoding];

        [data writeToFile:path atomically:YES];
    }
}
}

и мой метод загрузки:

-(Cookbook *) loadCookbook:(NSString *)path{
BOOL b = [[NSFileManager defaultManager] fileExistsAtPath:path];
NSLog(@"File exists: %i",b);

Cookbook* ret = nil;
NSData* data = [NSData dataWithContentsOfFile:path];
if(data){
    NSKeyedUnarchiver* unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
    int version = [unarchiver decodeIntForKey:@"Version"];
    if(version == 1){
        ret = (Cookbook*) [unarchiver decodeObjectForKey:@"Cookbook"];
    }
    [unarchiver finishDecoding];
}
return ret;
}

У меня также есть метод сохранения и загрузки для моего класса Recipe, очень похожий на этот.

Еще раз, любая помощь будет принята с благодарностью, и спасибо, что нашли время, чтобы прочитать это.

РЕДАКТИРОВАТЬ: Вот метод encodeWithCoder в Recipe.m с несколькими переменными, опущенными для краткости:

-(void) encodeWithCoder:(NSCoder *)aCoder{
NSLog(@"Encoding Recipe.");
[aCoder encodeObject:name forKey:@"name"];
[aCoder encodeObject:ingredients forKey:@"ingreds"];
[aCoder encodeObject:recipePicture forKey:@"recipePict"];
[aCoder encodeObject:instructions forKey:@"instructs"];
[aCoder encodeObject:category forKey:@"categ"];
[aCoder encodeObject:dateAdded forKey:@"dateAdd"];
[aCoder encodeInt:prepTime forKey:@"prepTime"];

}

и в Cookbook.m:

-(void) encodeWithCoder:(NSCoder *)aCoder{
NSLog(@"Encoding cookbook.");
[aCoder encodeObject:recipes forKey:@"recipes"];
}

person crarho    schedule 20.12.2012    source источник
comment
Вы сохраняете его с помощью кнопки сохранения, извлекаете его с помощью действия кнопки открытия, независимо от того, сколько классов обращаются к нему. Если вы хотите потокобезопасность, вы можете выбрать атомарность, однако атомарность не является полностью потокобезопасной.   -  person Anoop Vaidya    schedule 20.12.2012
comment
Может быть, не по теме, но я бы использовал основные данные. Тогда это будет легко, как пирог :)   -  person johan    schedule 20.12.2012
comment
Помимо вашего изображения, кажется, что все в значительной степени является строкой или целым числом или датой, или массивом или словарем одного и того же. JSON довольно хорошо подходит для этого — вам придется использовать специальный регистр изображения и дату в небольшой степени, но строки, числа, массивы и словари в значительной степени автоматические.   -  person Hot Licks    schedule 20.12.2012
comment
Что есть в ваших encodeWithCoder: реализациях?   -  person fguchelaar    schedule 20.12.2012
comment
Взгляните на мой ответ в этом, записывая объект в файл и читая обратно, вы можете почувствовать это удобным: stackoverflow.com/questions/13801222/   -  person Anoop Vaidya    schedule 20.12.2012
comment
Извините, но код длинный, я не все прочитал, вы спрашиваете, как это сделать, или часть кода не работает?   -  person Ramy Al Zuhouri    schedule 20.12.2012
comment
Было бы обидно, если бы вы прекратили разработку своего проекта. Вы, конечно, не все представили, но, кажется, он хочет только одну или две детали. Я был одним из тех, кто проголосовал за комментарий fguchelaar: предоставили ли вы полную реализацию протокола NSCoder для ваших объектов?   -  person Extra Savoir-Faire    schedule 20.12.2012
comment
Я также хотел бы сказать, что не думаю, что имеет смысл разрушать то, что вы сделали, только для того, чтобы реализовать это с помощью Core Data. Core Data не является панацеей от всего, как некоторые могут предположить.   -  person Extra Savoir-Faire    schedule 20.12.2012
comment
Я обновлю свой вопрос с помощью метода encodeWithCoder: в конце и рассмотрю ваши объяснения/ответы, Anoop.   -  person crarho    schedule 20.12.2012
comment
И для Рами: Иногда, когда я экспериментирую с разными способами получения пути к файлу, программа просто вылетает. Что касается моей основной проблемы, я просто не могу перезагрузить данные, которые я сохраняю. Я чувствую, что большая часть проблемы на самом деле заключается в пути, по которому я пытаюсь его сохранить, RecipeList.plist   -  person crarho    schedule 20.12.2012


Ответы (1)


Я «почти» скопировал ваш код, и после некоторых незначительных изменений он заработал.

В кулинарную книгу я добавил:

- (id)init {
    if (self = [super init]) {
        recipes = [NSMutableArray array];
    }
    return self;
}

и

- (void)addRecipe:(Recipe *)recipe {
    [recipes addObject:recipe];
}

Может быть, они у вас уже есть? Я предполагаю, что вы где-то создаете массив?

Затем, используя эту тестовую процедуру, кулинарная книга создается, используется и сохраняется отлично:

NSString *path = [self filePath];
Cookbook *cookbook = [self loadCookbook:path];

if (cookbook) {
    NSLog(@"Loaded cookbook: %@", cookbook);
}
else {
    cookbook = [[Cookbook alloc] init];
}

Recipe *recipe = [[Recipe alloc] init];
recipe->name = @"The name";
[cookbook addRecipe:recipe];

[self save:path cookbook:cookbook];
NSLog(@"Saved cookbook: %@", cookbook);

К сведению: для краткости я сделал переменные экземпляра рецепта @public; надеюсь, вы используете средства доступа (@property).

Вы используете автоматический или ручной подсчет ссылок?

person fguchelaar    schedule 21.12.2012
comment
Прошу прощения за поздний ответ, но в настоящее время я использую автоматический подсчет ссылок. Я попробую ваше предложение завтра, как только смогу, и дам вам знать, как оно идет. Я очень ценю помощь (я довольно долго застрял в этой проблеме и обнаружил, что буквально не мог добиться большего, не улучшив сначала эту функцию - самую важную функцию на сегодняшний день). - person crarho; 22.12.2012
comment
Я попробовал это, и по какой-то причине мой оператор NSLog(@file exists: %i,b) в моем методе loadCookbook: всегда говорит, что файл RecipeList.plist не существует. Казалось бы, это ядро ​​​​моей проблемы на данный момент; однако могу ошибаться. - person crarho; 22.12.2012
comment
Я бы предложил написать небольшой фрагмент кода, который просто создает файл в указанном вами месте. Посмотрите, работает ли это... Вы можете проверить содержимое файловой системы симуляторов в ~/Library/Application Support/iPhone Simulator/‹VERSION›/Applications/‹ID›/ - person fguchelaar; 23.12.2012