2009-10-21 15 views
10

Tengo una aplicación Core Data con un modelo de datos bastante simple. Quiero poder almacenar instancias de NSImage en la tienda persistente como objetos PNG Bitmap NSData, para ahorrar espacio.¿Por qué mi atributo de datos centrales transformable no utiliza mi NSValueTransformer personalizado?

Para este fin, escribí un NSValueTransformer simple para convertir un NSImage a NSData en formato de mapa de bits PNG. Estoy registrando el transformador valor con este código en mi aplicación delegado:

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

En mi modelo de datos, he puesto el atributo de imagen para ser transformable, y se especifica PNGDataValueTransformer como nombre del valor transformador.

Sin embargo, mi transformador de valor personalizado no se está utilizando. Lo sé porque he colocado mensajes de registro en los métodos de -transformedValue: y -reverseTransformedValue de mi transformador de valor que no se registran, y los datos que se guardan en el disco son solo un NSI archivado, no el PNG NSData objeto que debería ser.

¿Por qué no funciona?

Aquí está el código de mi valor transformador:

@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 

Respuesta

0

Si nada en su código en otros lugares explícitamente utiliza la clase PNGDataValueTransformer, entonces el método initialize + porque nunca se llamará esa clase. Especificar el nombre en su modelo de Datos Básicos tampoco lo activará; simplemente intentará buscar un transformador de valor para ese nombre, que devolverá cero, ya que aún no se ha registrado ninguna instancia de transformador con ese nombre.

Si esto es realmente lo que sucede en su caso, simplemente agregue una llamada a [PNGDataValueTransformer initialize] en algún lugar de su código antes de acceder a su modelo de datos, p. en el método de + inicialización de cualquier clase que esté usando este modelo de datos. Esto debería desencadenar la creación de la instancia del transformador de valor y registrarla para que Core Data pueda acceder a ella cuando lo necesite.

+0

Hola Brian, el '+ initialize' método es en mi aplicación delegado, no el transformador de valor y que sin duda puede verificar que está siendo llamado. Si pongo un punto de interrupción en el método '-init' de mi NSValueTransformer, definitivamente se inicializa. Si configuro el atributo para que sea del tipo "binario" y aplique mi transformador de valor a cada enlace en IB, funciona bien. Simplemente no entiendo por qué el transformador personalizado no se está utilizando cuando especifico el tipo de atributo como Transformable. –

25

Si no me equivoco, su transformador de valores tiene una dirección inversa. Yo hago lo mismo en mi solicitud, utilizando código como el siguiente:

+ (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]; 
} 

Si bien esto es para el iPhone, usted debería ser capaz de cambiar su código NSImage en los lugares apropiados. Simplemente, aún no he probado la implementación de mi Mac.

Todo lo que hice para habilitar esto fue establecer que la propiedad de mi imagen fuera transformable dentro del modelo de datos y especificar el nombre del transformador. No necesité registrar manualmente el transformador de valor, como lo hace en su método de + inicialización.

+0

Gracias Brad. Intenté esto y no hizo ninguna diferencia, mi transformador de valor simplemente se ignora. –

2

Parece que el registro del transformador no tiene ningún efecto sobre si Core Data lo usará o no. He estado jugando con el código de muestra de PhotoLocations y eliminar el registro del transformador no tiene ningún efecto. Siempre que su ValueTransformerName sea el nombre de su clase, y que la implementación de su transformador esté incluida con el objetivo, debería funcionar.

Si no hay ejecución de código en el transformador, entonces el código en el transformador es irrelevante. El problema debe estar en otro lugar en la pila de datos del núcleo, o en la declaración del transformador.

+0

¡Lo mismo aquí! ¿Encontraste una solución? – webmastx

0

Consulte el código de ejemplo here.

¿Hace esto lo que quiere?

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

Es necesario registrar explícitamente su transformador durante el tiempo de ejecución.

Un buen lugar para hacer esto es anular el método de inicialización de clase en su subclase de entidad NSManagedObject. Como se mencionó anteriormente, se conoce la falla de Core Data. A continuación se muestra el código fundamental de la ubicación de la muestra el código de Apple, que se ha probado y funciona: 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"]; 
    } 
} 
+0

Sí, sé esto y lo estoy haciendo. –

+0

En mi caso, fue exactamente el problema. También puede verificar si el nombre del transformador coincide con el que declaró en el modelo de datos y si no ha invertido los cuerpos de los métodos: transformValue y reverseTransformedValue. – Lukasz

Cuestiones relacionadas