代码之家  ›  专栏  ›  技术社区  ›  Rob Keniger

为什么我的可转换核心数据属性不使用自定义NSValueTransformer?

  •  10
  • Rob Keniger  · 技术社区  · 16 年前

    我有一个核心数据应用程序,它有一个相当简单的数据模型。我希望能够将nsimage的实例存储在持久存储中,作为PNG位图nsdata对象,以节省空间。

    为此,我编写了一个简单的nsValueTransformer,将nsImage转换为PNG位图格式的nsData。我正在应用程序委托中使用此代码注册值转换器:

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

    在我的数据模型中,我已经将image属性设置为可转换并指定 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 回复  |  直到 13 年前
        1
  •  25
  •   Brad Larson    16 年前

    如果我没弄错的话,你的价值转换器有一个相反的方向。我在我的应用程序中做同样的事情,使用如下代码:

    + (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实现。

    我所做的一切就是将Image属性设置为在数据模型中可转换,并指定转换器的名称。我不需要像在+initialize方法中那样手动注册值转换器。

        2
  •  3
  •   CornPuff    16 年前

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

    如果变压器中没有代码执行,那么变压器中的代码是无关的。问题必须出现在核心数据栈的其他地方,或者变压器的声明中。

        3
  •  2
  •   Rob Keniger    16 年前

    事实证明,这实际上是框架中的一个bug。请参阅Cocoa Dev邮件列表中苹果员工的这篇文章:

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

        4
  •  1
  •   Lukasz    13 年前

    您需要在运行时显式注册转换器。

    这样做的好地方是重写nsmanagedObject实体子类中的类初始化方法。如前所述,这就是所谓的核心数据错误。以下是来自苹果公司位置代码示例的关键代码,它经过测试和工作: 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"];
        }
    }
    
        5
  •  0
  •   Brian Webster    16 年前

    如果其他地方的代码中没有明确使用pngDataValueTransformer类,则永远不会调用该类的+initialize方法。在核心数据模型中指定名称也不会触发它——它只会尝试查找该名称的值转换器,它将返回nil,因为还没有以该名称注册任何转换器实例。

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

        6
  •  0
  •   RyanJM    16 年前

    查看示例代码 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];
    }