Сохранение дополнительных данных, добавленных в расширение класса

Я создал класс UIImage, который также содержит данные о координатах того, где должно быть отрисовано изображение:

#import "UIImageExtras.h"
#import <objc/runtime.h>

@implementation UIImage (Extras)

static char UII_ORIGINDATA_KEY;

@dynamic originData;

- (void)setOriginData:(NSValue *)originData {
    objc_setAssociatedObject(self, &UII_ORIGINDATA_KEY, originData, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSValue *)originData {
    return (NSValue *)objc_getAssociatedObject(self, &UII_ORIGINDATA_KEY);
}

Пока все хорошо. У меня есть класс, содержащий массив таких изображений. Класс называется BookDoc, а массив — illustrations. Когда я копирую экземпляр BookDoc, я копирую массив. Исходные данные, которые я определил для каждого изображения, копируются нормально.

Однако, когда я затем сохраняю эту новую копию в файл, я теряю originData. Это мой метод сохранения:

- (void)saveIllustrations {
if (_illustrations == nil) {
    NSLog(@"Nil array");
    return;
}

[self createDataPath];
NSString *illustrationsArrayPath = [_docPath stringByAppendingPathComponent:kIllustrationsFile];
BOOL result = [NSKeyedArchiver archiveRootObject:_illustrations toFile:illustrationsArrayPath];
if (!result)
    NSLog(@"Failed to archive array");

//This is not saving the originData. 

self.illustrations = nil;

}

Мой вопрос: как я могу обеспечить сохранение originData для каждого изображения? Большое спасибо.

ОБНОВИТЬ:

Хорошо, я перешел к подклассу UIImage в классе с именем UIImageExtra и привел его в соответствие с NSCoding следующим образом:

    - (void)encodeWithCoder:(NSCoder *)aCoder {
    NSLog(@"Encoding origin data!");
    [aCoder encodeObject:originData forKey:kOriginData];
    [super encodeWithCoder:aCoder];
}

    - (id)initWithCoder:(NSCoder *)aDecoder {
        if (self = [super initWithCoder:(NSCoder *) aDecoder]) {
            NSLog(@"Decoding origin data");
            self.originData = [aDecoder decodeObjectForKey:kOriginData];
        }
        return self;
    }

Теперь, когда я сохраняю массив иллюстраций, содержащий эти экземпляры UIImageExtra, не должен ли он автоматически сохранять исходные данные? Мой код для сохранения массива выглядит так:

- (void)saveIllustrations {
if (_illustrations == nil) {
    NSLog(@"Nil array");
    return;
}

[self createDataPath];
NSString *illustrationsArrayPath = [_docPath stringByAppendingPathComponent:kIllustrationsFile];
BOOL result = [NSKeyedArchiver archiveRootObject:_illustrations toFile:illustrationsArrayPath];
if (!result)
    NSLog(@"Failed to archive array");

//This is not saving the originData. 

self.illustrations = nil;

}


person Smikey    schedule 02.05.2012    source источник


Ответы (1)


Я не думаю, что вы можете сделать это для свойств, добавленных через связанный объектный API, потому что UIImage encodeWithCoder: и initWithCoder: понятия не имеют, что ваши свойства там.

Если бы вы могли создать свой собственный MyUiImage, унаследовав вместо этого UIImage, вы могли бы переопределить encodeWithCoder: и initWithCoder: своего класса и кодировать/декодировать свои свойства вместе с UIImage.

person Sergey Kalinichenko    schedule 02.05.2012
comment
О, какой позор... Наследование не так удобно. Но я думаю, это имеет смысл, спасибо. - person Smikey; 02.05.2012
comment
Я обновил свой ответ, не могли бы вы взглянуть еще раз? - person Smikey; 02.05.2012
comment
@Smikey Ваше обновление должно работать. Я предполагаю, что вы явно приняли протокол <NSCoding> в своем подклассе, верно? Также вы можете переместить [super encodeWithCoder:aCoder]; впереди остального кода, чтобы соответствовать примерам Apple (хотя я не думаю, что это имеет значение в данном случае). - person Sergey Kalinichenko; 02.05.2012
comment
Да, NSCoding. Я добавил операторы NSLog в сообщения encodeWithCoder/initWithCoder, но они никогда не печатаются. Вместо этого я попробовал: [self createDataPath]; NSString *illustrationsArrayPath = [_docPath stringByAppendingPathComponent:kIllustrationsFile]; NSMutableData *data = [[NSMutableData alloc] init]; NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data]; [архиватор encodeObject:_illustrations forKey:kIllustrationDataKey]; [архиватор FinishEncoding]; [данные writeToFile:illustrationsArrayPath атомарно: YES]; - person Smikey; 02.05.2012
comment
... но это все еще не работает. Добавленная мной переменная originData всегда имеет значение null. - person Smikey; 02.05.2012
comment
@Smikey Это действительно очень странно. Разве вы не видите NSLog как для кодирования, так и для декодирования?.. Не могли бы вы попробовать закодировать только первый объект из массива и посмотреть, получите ли вы журнал хотя бы для encodeWithCoder:? - person Sergey Kalinichenko; 02.05.2012
comment
А, кажется, я знаю, почему. Я задал его как отдельный вопрос - stackoverflow. ком/вопросы/10415111/. Это потому, что мой массив на самом деле не содержит объектов UIImageExtra из-за того, как я его построил. Хотя все еще не совсем уверен, как лучше всего создавать экземпляры UIImageExtra... Но спасибо за помощь! - person Smikey; 02.05.2012
comment
@Smikey А, я вижу, что происходит. Вы можете определить метод класса +(UIImageExtra*)imageNamed: в своем новом классе, а также переопределить -(id)imageWithContentsOfFile:. +(UIImageExtra*)imageNamed: вызовет imageWithContentsOfFile:, который вызовет imageWithContentsOfFile: из UIImage, а затем установит дополнительные поля, которые вы добавили. - person Sergey Kalinichenko; 02.05.2012
comment
Хорошо, я вроде вижу, но я все еще немного смущен. Вы имеете в виду initWithContentsOfFile, а не «изображение»? Должен ли я не делать {+(UIImageExtra *)initWithUIImage:(UIImage *)image} Но тогда я не уверен, что делать с UIImage, который я передаю... Или если я переопределю и вызову {- (id)initWithContentsOfFile: (NSString *) path { UIImage * image = [super initWithContentsOfFile: path]; возврат изображения; }} Затем я получаю предупреждение компилятора о том, что несовместимые типы указателей инициализируют 'UIImageExtra *__strong' выражением типа 'UIImage *' Гарг, я так запутался, о_О - person Smikey; 02.05.2012
comment
@Smikey Внутри initWithContentsOfFile вы делаете self = [super initWithContentsOfFile:path]; if (self) ... и так далее, следуя обычному шаблону init. - person Sergey Kalinichenko; 02.05.2012