Почему мой трансформируемый атрибут Core Data не использует мой собственный NSValueTransformer?
У меня есть приложение Core Data с довольно простой моделью данных. Я хочу иметь возможность хранить экземпляры NSImage в постоянном хранилище в виде объектов PND Bitmap NSData, чтобы сэкономить место.
С этой целью я написал простой NSValueTransformer для преобразования NSImage в NSData в растровом формате PNG. Я регистрирую преобразователь значения с помощью этого кода в моем делегате приложения:
+ (void)initialize
{
[NSValueTransformer setValueTransformer:[[PNGDataValueTransformer alloc] init] forName:@"PNGDataValueTransformer"];
}
В моей модели данных я установил атрибут изображения как Transformable, и указал PNGDataValueTransformer
как имя преобразователя значения.
Тем не менее, мой пользовательский преобразователь значения не используется. Я знаю это, поскольку я разместил сообщения журнала в моем преобразователе значения -transformedValue:
а также -reverseTransformedValue
методы, которые не регистрируются, и данные, которые сохраняются на диск, представляют собой просто архивированный NSImage, а не объект PNG NSData, каким он должен быть.
Почему это не работает?
Вот код моего преобразователя значения:
@implementation PNGDataValueTransformer
+ (Class)transformedValueClass
{
return [NSImage class];
}
+ (BOOL)allowsReverseTransformation
{
return YES;
}
- (id)transformedValue:(id)value
{
if (value == nil) return nil;
if(NSIsControllerMarker(value))
return value;
//check if the value is NSData
if(![value isKindOfClass:[NSData class]])
{
[NSException raise:NSInternalInconsistencyException format:@"Value (%@) is not an NSData instance", [value class]];
}
return [[[NSImage alloc] initWithData:value] autorelease];
}
- (id)reverseTransformedValue:(id)value;
{
if (value == nil) return nil;
if(NSIsControllerMarker(value))
return value;
//check if the value is an NSImage
if(![value isKindOfClass:[NSImage class]])
{
[NSException raise:NSInternalInconsistencyException format:@"Value (%@) is not an NSImage instance", [value class]];
}
// convert the NSImage into a raster representation.
NSBitmapImageRep* bitmap = [NSBitmapImageRep imageRepWithData: [(NSImage*) value TIFFRepresentation]];
// convert the bitmap raster representation into a PNG data stream
NSDictionary* pngProperties = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:NO] forKey:NSImageInterlaced];
// return the png encoded data
NSData* pngData = [bitmap representationUsingType:NSPNGFileType properties:pngProperties];
return pngData;
}
@end
6 ответов
Оказывается, это на самом деле ошибка в рамках. Смотрите этот пост сотрудника Apple в списке рассылки Cocoa-Dev:
http://lists.apple.com/archives/Cocoa-dev/2009/Dec/msg00979.html
Если я не ошибаюсь, ваше значение преобразователя имеет обратное направление. Я делаю то же самое в своем приложении, используя код, подобный следующему:
+ (Class)transformedValueClass
{
return [NSData class];
}
+ (BOOL)allowsReverseTransformation
{
return YES;
}
- (id)transformedValue:(id)value
{
if (value == nil)
return nil;
// I pass in raw data when generating the image, save that directly to the database
if ([value isKindOfClass:[NSData class]])
return value;
return UIImagePNGRepresentation((UIImage *)value);
}
- (id)reverseTransformedValue:(id)value
{
return [UIImage imageWithData:(NSData *)value];
}
Хотя это для iPhone, вы должны иметь возможность поменять код NSImage в соответствующих местах. Я просто еще не тестировал свою реализацию Mac.
Все, что я сделал, чтобы включить это, - установил, что мое свойство изображения должно преобразовываться в модели данных, и указал имя преобразователя. Мне не нужно было вручную регистрировать преобразователь значения, как вы делаете это в вашем методе + initialize.
Кажется, что регистрация трансформатора не влияет на то, будут ли Core Data использовать это или нет. Я играл с примером кода PhotoLocations и удаление регистрации трансформатора не имеет никакого эффекта. Пока ваше ValueTransformerName является именем вашего класса и что реализация вашего преобразователя включена в цель, она должна работать.
Если в преобразователе нет выполнения кода, то код в преобразователе не имеет значения. Проблема должна быть где-то еще в основном стеке данных или в объявлении преобразователя.
Вы должны явно зарегистрировать свой преобразователь во время выполнения.
Хорошее место для этого - переопределить метод инициализации класса в вашем подклассе сущности NSManagedObject. Как упоминалось ранее, это известная ошибка Core Data. Ниже приведен важный код из примера кода местоположения Apple, он протестирован и работает: http://developer.apple.com/library/ios/
+ (void)initialize {
if (self == [Event class]) {
UIImageToDataTransformer *transformer = [[UIImageToDataTransformer alloc] init];
[NSValueTransformer setValueTransformer:transformer forName:@"UIImageToDataTransformer"];
}
}
Проверьте пример кода здесь.
Делает ли это то, что вы хотите?
@implementation UIImageToDataTransformer
+ (BOOL)allowsReverseTransformation {
return YES;
}
+ (Class)transformedValueClass {
return [NSData class];
}
- (id)transformedValue:(id)value {
NSData *data = UIImagePNGRepresentation(value);
return data;
}
- (id)reverseTransformedValue:(id)value {
UIImage *uiImage = [[UIImage alloc] initWithData:value];
return [uiImage autorelease];
}
Если в вашем коде в другом месте ничего не используется explicity, используется класс PNGDataValueTransformer, то метод +initialize для этого класса никогда не будет вызываться. Указание имени в вашей модели базовых данных также не вызовет его - оно просто попытается найти преобразователь значения для этого имени, который вернет nil, поскольку ни один экземпляр преобразователя еще не был зарегистрирован под этим именем.
Если это действительно то, что происходит в вашем случае, просто добавьте вызов [PNGDataValueTransformer initialize] где-то в вашем коде до того, как ваша модель данных будет доступна, например, в методе +initialize любого класса, который использует эту модель данных. Это должно инициировать создание и регистрацию экземпляра преобразователя значений, чтобы базовые данные могли получить к нему доступ, когда это необходимо.