在 Core Data 中存取 transformable 类型的数据
本文转载至 http://imenjoe.com/2015/04/10/CoreData-transformable-20150410/
在开发过程中有一个需要在 Core Data 中存取 NSDictionary 的需求,但是在 Core Data 的 attributeType 中并没有直接支持 NSDictionary 的类型,基本上就是一些 scalar 类型和 date、binary data、undefined 和 transformable 类型。
最初的想法是把 NSDictionary 转为 NSData 然后用 binary data 的类型写入到 Core Data,然后要用的时候再从 Core Data 中读出 NSData,再转回 NSDictionary,但是想想这样其实也挺麻烦的,想看看 Core Data 有没有更好的对这类需求的支持,这个方法就作为最后的手段来使用吧。于是在扫了一遍 Core Data 的 attributeType 之后就盯上了 transformable 这个类型。
在 Apple 开发者文档中对 transformable 的描述是:
The idea behind transformable attributes is that you access an attribute as a non-standard type, but behind the scenes Core Data uses an instance of NSValueTransformer to convert the attribute to and from an instance of NSData. Core Data then stores the data instance to the persistent store.
大概意思就是把上面提到的想法隐藏在了 transformable 之下,使之可以直接用 non-standard 类型(在我们的例子中就是 NSDictionary)透明地访问 Core Data 中的 attribute。整个转换和持久化的过程都由 Core Data 自动完成。
这个转换过程 Core Data 需要一个 transformer 实例来进行 non-standard 类型跟 NSData 之间的转换,这个 transformer 实例是抽象类 NSValueTransform 的实现的一个实例,这个类的作用在于把一个值转换为另一个值,这个超出了这次的讨论范围,关于 NSValueTransform 可以参考这篇文章。
使用
要使用 transformable 类型有两种方式:
如果是在 .xcdatamodeld 中编辑,在 Type 的下拉菜单中选择 transformable 类型,在这个 attribute 的属性页的 Name 文本框中填 transformer name(如果要自定义 transformer 的话才需要填,否则就使用 Core Data 默认的)
如果是用代码写的,那么调用 setAttributeType: 时传入 NSTransformableAttributeType 作为参数,然后(如果需要的话)用 setValueTransformerName: 来指定 transformer 的 name。
我使用的是第一种方式,直接选择了 attribute 的 Type 为 transformable 后就完成了。由于 value transformer name 的文本框为空,所以 Core Data 会默认使用 NSKeyedUnarchiveFromDataTransformerName 为 transformer。这个 transformer 其实就是调用 NSKeyArchiver 来对 non-standard 类型转换。整个过程大概类似于这样:
NSDictionary -> NSData
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:myDictionary];
NSData -> NSDictionary
NSDictionary *dictionary = (NSDictionary*)[NSKeyedUnarchiver unarchiverObjectWithData:data];
由于我要存取的 NSDictionary 是直接支持用 NSKeyedArchiver 来转换的,所以我没有自定义 transformer,直接用默认的 transformer 就可以满足我的需要了。
接下来就是重新生成实体类,由于我用的是 mogenerator 来生成的,要让 mogenerator 在生成的实体类中对应的 attribute 是我想要的 NSDcitionary,那么还需要在 .xcdatamodeld 中给 attribute 的 User Info 添加一项
| Key | Value | |
|---|---|---|
| attributeValueClassName | NSDictionary |

mogenerator 在生成实体类时就会从这里获取到信息,知道这个 attribute 要转换的 non-standard 是 NSDictionary,在生成的实体类中对应的 property 就会是 NSDictionary,从而使得我们可以直接以 NSDictionary 类型对这个 property 进行读写,而完全不用管底下的转换和存储的过程。
transformable 类型除了可以存取 NSDictionary 类型之外,还可以存取 UIColor、CGRect 等等类型,总之如果在 Core Data 提供的标准类型中找不到自己想要的类型,那么就可以用 transformable 来进行存取,方便快捷。
扩展
到这里其实这个问题就已经算是解决了,但是我们在上面提到了一个自定义 transformer,这里我们还可以对这个扩展一下。
上面提到了 transformable 可以存取 NSDictionary 之外的其他的 non-standard 类型,例如 Color、Image 之类的,但是如果用的时默认的 transformer 的话,那么这个 non-standard 必须实现 NSCoding 协议,才能将其实例序列化为 NSData,并且进行存取。我之所以直接用默认的 transformer 也是因为 NSDictionary 实现了 NSCoding 协议的。但并不是所有的 non-standard 类型都实现了 NSCoding 协议的,要存取这些没有实现 NSCoding 协议的类型怎么办?
这个时候就轮到自定义的 transformer 出场了,上面提到过 transformer 其实就是 NSValueTransform 抽象类的实现的一个实例,而这个类的作用就是把一个值按照指定的规则转换为另一个值,所以我们可以通过实现 NSValueTransform 来指定规则,创建实例来完成我们的自定义 transformer。
关于自定义 transformer 在 Apple 开发者文档上是这样描述的:
If you specify a custom transformer, it must transform an instance of the non-standard data type into an instance of NSData and support reverse transformation.
如果只是需要单向转换的话,那么只需要实现这几个方法即可:
+ (Class) transformedValueClass;
+ (BOOL) allowsReverseTransformation;
- (id) transformedValue:(id)value;
如果还要支持反向转换的话,那么还需要实现这个方法:
- (id)reverseTransformedValue:(id)value;
由于 Core Data 的 transformable 是需要支持双向转换的(从 non-standard type 到 NSData,还有从 NSData 到 non-standard type),所以上面的方法都要实现,下面是我根据网上两个例子( SO 和 Eugene’s blog) 改造而来的一个例子:
#import <UIKit/UIKit.h>
@interface UIImageToNSDataTransformer : NSValueTransformer
{
}
@end
#import "UIImageToNSDataTransformer.h"
@implementation UIImageToNSDataTransformer
+ (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];
}
@end
实现了 NSValueTransform 后,最后要做的就是在 .xcdatamodeld 的 transformable attribute 的属性页中,把这个自定义 transformer 的类名填写在 Name 文本框中。
自定义的 transformer 还可以引申出另一个应用,就是可以用于对 entity 中某个 attribute 进行加密/解密,用自定义的加密/解密算法。单独对某个 attribute 进行加密/解密,可以避免对整个 entity 甚至是整个 database 进行加密/解密,提高了性能,降低了内存消耗。这个在 SO 这个答案中有一些说明。
(注:对于 NSValueTransform 的使用,在 Mantle 库的NSValueTransformer +MTLPredefinedTransformerAdditions、MTLValueTransformer 等类中有很好地学习例子,还有 mattt 的 TransformerKit 库也是。)
在 Core Data 中存取 transformable 类型的数据的更多相关文章
- “System.Data”中不存在类型或命名空间名称“TypedTableBase”
错误 1 命名空间“System.Data”中不存在类型或命名空间名称“TypedTableBase”(是否缺少程序集引用?) 解决方案 因为是把强类型DataSet文件绑定报表的项目中出现的错误, ...
- ASP.NET#命名空间"System.Data"中不存在类型或命名空间名称"Linq"(是否缺少程序集引用?)
添加完.dbml(LINQ to SQL类文件)文件后,双击.designer.cs源文件时,发现编译器提示:命名空间"System.Data"中不存在类型或命名空间名称" ...
- iOS教程:如何使用Core Data – 预加载和引入数据
这是接着上一次<iOS教程:Core Data数据持久性存储基础教程>的后续教程,程序也会使用上一次制作完成的. 再上一个教程中,我们只做了一个数据模型,之后我们使用这个数据模型中的数据创 ...
- 向mysql中插入Date类型的数据
先看数据库表的定义 date字段为sql.date类型.我要向其中插入指定的日期和当前日期. 一.插入当前日期 思路:先获取当前系统,在将当前系统时间转换成sql类型的时间,然后插入数据库.代码如下 ...
- iOS:Core Data 中的简单ORM
我们首先在xcdatamodel文件中设计我们的数据库:例如我建立一个Data的实体,里面有一个String类型的属性name以及一个Integer类型的num: 然后选中Data,添加文件,选择NS ...
- 再看Core Data中PSC陷入死锁的问题
在<Core Data Programming Guide>文档的Concurrency with Core Data这一章节中提到了“Use Thread Confinement to ...
- SQL中的float类型的数据
问题1. 如何在SQL中默认的使用float类型的数据 SQL中想要通过计算的方式最快的得到一个float类型的数据,只需要运算的其中一个值后面加上小数点就ok. 比如 :9/2=4 但是 :9/2 ...
- 如何在R中导入不同类型的数据
这个表格是我在datacamp学习R导入文件的课程的归纳 遇到的问题及解决方法(环境: Rv3.2.5,win7,32位) 1. 使用gdata中的read.xls时提示找不到Perl路径 >l ...
- django -- model中只有Field类型的数据才能成为数据库中的列
一.model的定义: from django.db import models # Create your models here. class Person(models.Model): firs ...
随机推荐
- Python之输出当前时间
import time print time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))
- [转]Idea2016 使用Maven配置简单Web项目(受益比较多的一篇)
最近被同事一直吵着用Idea写Java,于是偷偷的去试用了一下Idea.确实不错,无论界面还是智能提醒都是蛮符合我的使用习惯,但是刚从Eclipse出来,使用Idea还是不太习惯的.所以这里写出来,供 ...
- Android WiFi 日志记录(四次握手)
记录一下四次握手的log. PMK: PMK(Pairwise Master Key,成对主密钥 STA和AP得到PMK后,将进行密匙派生以得到PTK.最后,PTK被设置到硬件中, 用于数据的加解密. ...
- application.properties详解 --springBoot配置文件
本文转载:http://blog.csdn.net/lpfsuperman/article/details/78287265###; # spring boot application.propert ...
- python程序的输入输出(acm的几个小程序)
1, A+B Problem : http://acm.sdut.edu.cn/sdutoj/problem.php?action=showproblem&problemid=1000 #! ...
- spring中基于aop使用ehcache
我就是对着这个博客看的 http://www.cnblogs.com/ctxsdhy/p/6421016.html
- Drools 语法
Drools 语法 规则语法 package: package 的名字是随意的,不必必须对应物理路径 import: 导入外部变量 规则的编译与运行要通过Drools 提供的各种API 来实现.API ...
- android 静默安装 卸载 资料汇总
1. android + eclipse + 后台静默安装(一看就会) 2. 适用于android1.5以下版本apk静默安装 3. error: INSTALL_FAILED_SHARED_USER ...
- php public,static,private,protected,final,const,abstract
public:权限是最大的,可以内部调用,实例调用等. protected: 受保护类型,用于本类和继承类调用. private: 私有类型,只有在本类中使用. final:PHP 5:不被改,不被继 ...
- Robot Framework分层、开发系统关键字
开发系统关键字:http://www.cnblogs.com/fnng/p/4261293.html http://www.cnblogs.com/fnng/p/3969978.htm ...