【问题标题】:Core Data, Bindings, value transformers : crash when saving核心数据、绑定、值转换器:保存时崩溃
【发布时间】:2010-05-21 22:35:09
【问题描述】:

我正在尝试将 PNG 图像存储在由 sqlite 数据库支持的核心数据存储中。因为我打算在 iPhone 上使用这个数据库,所以我不能直接存储 NSImage 对象。我想使用绑定和NSValueTransformer 子类来处理从NSImage(通过我的GUI 上的图像井获得)到包含图像的PNG 二进制表示的NSData 的转码。我为NSValueTransformer 编写了以下代码:


+ (Class)transformedValueClass
{
    return [NSImage class];
}

+ (BOOL)allowsReverseTransformation
{
    return YES;   
}

- (id)transformedValue:(id)value
{
    if (value == nil) return nil;

    return [[[NSImage alloc] initWithData:value] autorelease];
}

- (id)reverseTransformedValue:(id)value
{
    if (value == nil) return nil;

 if(![value isKindOfClass:[NSImage class]])
 {
  NSLog(@"Type mismatch. Expecting NSImage");
 }

 NSBitmapImageRep *bits = [[value representations] objectAtIndex: 0];

 NSData *data = [bits representationUsingType:NSPNGFileType properties:nil];

    return data;
}

模型有一个使用此NSValueTransformer 配置的可转换属性。在 Interface Builder 中,表格列和图像井都绑定到此属性,并且都具有正确的值转换器名称(放置在图像井中的图像显示在表格列中)。每次添加图像或重新加载行时都会注册并调用转换器(通过NSLog() 调用进行检查)。当我尝试保存托管对象时会出现问题。控制台输出显示错误消息:

[NSImage 长度]:无法识别的选择器发送到实例 0x1004933a0

似乎核心数据正在使用值转换器从NSData 获取NSImage,然后尝试保存NSImage 而不是NSData

可能有一些解决方法,例如this post 中提出的解决方法,但我真的很想了解为什么我的方法有缺陷。

提前感谢您的想法和解释。

【问题讨论】:

    标签: objective-c image sqlite core-data cocoa-bindings


    【解决方案1】:

    我最终让它工作了(最好睡在上面)。有两个问题。第一个是我在 IB 绑定和核心数据模型中指定了相同的值转换器,这是多余的。

    我的理解是,IB 绑定指定的值转换器实际上是在视图和控制器之间转换值,而核心数据值转换器充当托管对象和持久存储之间的中间人。

    更糟糕的是,第二个问题是我的价值转换器颠倒了。 transformValue 消息在对象被保存(托管对象 -> 存储)时发送,reverseTransformedValue 在从存储加载值时发送,这与将视图对象绑定到模型对象时使用的转换器的方向有点相反.我从core data programming guide 得到了提示,您可以在其中阅读以下有点神秘的注释:

    重要提示:虽然默认的transformer是NSKeyedUnarchiveFromDataTransformerName指定的transformer,但是这个transformer其实是反向使用的。如果你明确指定默认转换器,Core Data 会“以错误的方向”使用它。

    尽管到目前为止我的程序正在运行,但我希望得到具有核心数据经验的人的确认,如果您能指出我可能错过的有关该问题的一些文档,我将不胜感激。

    提前致谢。

    工作代码:

    
    + (Class)transformedValueClass
    {
        return [NSData class];
    }
    
    + (BOOL)allowsReverseTransformation
    {
        return YES;   
    }
    
    - (id)reverseTransformedValue:(id)value
    {
        if (value == nil) return nil;
    
        return [[[NSImage alloc] initWithData:value] autorelease];
    }
    
    - (id)transformedValue:(id)value
    {
        if (value == nil) return nil;
    
        if(![value isKindOfClass:[NSImage class]])
        {
            NSLog(@"Type mismatch. Expecting NSImage");
        }
    
        NSBitmapImageRep *bits = [[value representations] objectAtIndex: 0];
    
        NSData *data = [bits representationUsingType:NSPNGFileType properties:nil];
    
        return data;
    }
    
    

    【讨论】:

    • 我没有两次指定变压器,并且遇到了与您相同的错误。反转transformedValue: 和reverseTransformedValue: 成功了。非常感谢!
    【解决方案2】:

    在transformedValue 中:您直接从NSData 到NSImage,但在reverseTransformedValue 中:您从NSImage 到NSBitmapImageRep 到NSData。所以两者不能一起工作,因为他们使用不同的方法。模型中的数据实际上是 NSBitmapImageRep 数据,而不是 NSImage 数据。两者必须完全相反。

    顺便说一句,我最近在另一篇文章中为此发布了一个转换器。你可以看到它here。当然,您根本不需要自定义转换器,因为您可以使用 NSKeyedArchiver。 NSImage 遵循 NSCoder 协议,因此它会为您处理转换。

    【讨论】:

    • 感谢您的回答,我同时在写自己的答案,所以我错过了您的。尽管我确实需要存储 PNG 表示,但那将是诀窍,因为 iPhone SDK 使用 UIImage 而不是 NSImage。简单的无变压器解决方案在 Mac 上运行良好,但在 iPhone 上读取 sqlite 数据库时会中断。
    • 是的,我自己使用了一个自定义转换器,因为我想在将图像存储到核心数据之前将它们调整为小尺寸。我允许某人将图像很好地拖动到图像上,因此可能会添加一个大图像并且我不想存储这么大的图像......因此我的变压器(尽管我删除了我发布的变压器中调整大小的图像) .
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-18
    相关资源
    最近更新 更多