为什么我的可转换Core Data属性不使用我的自定义NSValueTransformer?

时间:2009-10-21 03:34:12

标签: objective-c cocoa image core-data

我有一个Core Data应用程序,它具有相当简单的数据模型。我希望能够将NSImage的实例作为PNG Bitmap NSData对象存储在持久存储中,以节省空间。

为此,我编写了一个简单的NSValueTransformer,将NSImage转换为PNG位图格式的NSData。我正在我的App委托中使用此代码注册值转换器:

+ (void)initialize
{
    [NSValueTransformer setValueTransformer:[[PNGDataValueTransformer alloc] init] forName:@"PNGDataValueTransformer"];
}

在我的数据模型中,我将image属性设置为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 个答案:

答案 0 :(得分:25)

如果我没弄错的话,你的价值转换器方向相反。我在我的应用程序中执行相同的操作,使用如下代码:

+ (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方法中那样手动注册值转换器。

答案 1 :(得分:3)

似乎注册变压器对核心数据的使用与否没有影响。 我一直在玩PhotoLocations示例代码,删除变压器注册没有任何效果。只要您的ValueTransformerName是您的类的名称,并且您的变换器的实现包含在目标中,它就应该有效。

如果变压器中没有代码执行,则变压器中的代码无关紧要。问题必须在核心数据堆栈中的其他位置,或者在变换器的声明中。

答案 2 :(得分:2)

事实证明,这实际上是框架中的一个错误。请参阅Cocoa-Dev邮件列表上的Apple员工发表的这篇文章:

http://lists.apple.com/archives/Cocoa-dev/2009/Dec/msg00979.html

答案 3 :(得分:1)

您需要在运行时明确注册变换器。

这样做的好地方是覆盖NSManagedObject实体子类中的Class initialize方法。如前所述,它是已知的Core Data bug。以下是Apple的位置代码示例中的关键代码,经过测试和工作: http://developer.apple.com/library/ios/#samplecode/Locations/Introduction/Intro.html

+ (void)initialize {
    if (self == [Event class]) {
        UIImageToDataTransformer *transformer = [[UIImageToDataTransformer alloc] init];
        [NSValueTransformer setValueTransformer:transformer forName:@"UIImageToDataTransformer"];
    }
}

答案 4 :(得分:0)

如果您的代码中没有任何内容显式使用PNGDataValueTransformer类,则永远不会调用该类的+ initialize方法。在Core Data模型中指定名称也不会触发它 - 它只会尝试查找该名称的值转换器,该值将返回nil,因为尚未在该名称下注册变换器实例。

如果确实发生了这种情况,只需在访问数据模型之前在代码中的某处添加对[PNGDataValueTransformer initialize]的调用,例如:在任何类的+ initialize方法中使用此数据模型。这应该触发正在创建和注册的值转换器实例,以便Core Data可以在需要时访问它。

答案 5 :(得分:0)

查看示例代码here

这样做你想要的吗?

@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];
}