- 参考书籍:CORE DATA by Tutorials
- 默认有swift基础。
- 默认已阅读上一篇内容。
亲爱的们,上一篇我们简单的讲了Core Data的存储以及读取,我们已经说过了Core Data是对象图形化管理模式,既然对象放在最前面,那么在Core Data中最正确的操作方式应该是使用对象和属性来进行操作。
这一篇的主要内容:
- 使用所有的Attributes的类型。
- 使用对象来进行储存以及读取数据。
- data model的检验限制功能。
(1)、使用所有的Attributes的类型
在这一篇中我们也用一个Demo来演示,Demo如图所示:
那块绿色表示最喜欢的颜色。
那只斯派克表示头像。
(一切从简,以CoreData操作为核心。)
在name和age的输入框输入数据并且选择好日期后,点击Add按钮会将name、年龄、最喜欢的颜色、生日、头像全部保存起来。
点击show按钮会跳转到下一界面,如图:
在这个页面中会显示之前保存的数据。
在上一篇中我们知道在进行Core Data操作之前我们得先建立data model(数据模板),对此有问题的同学请参考[http://www.jianshu.com/p/96e2b321449c]
这是我们建立的数据模型:
Note:你应该发现了在.xcdatamodeld中Attributes(属性)的Type(类型)中有三种整形:Integer 16, Integer 32 和 Integer 64。* 区别就在于占据多大内存,每种类型储存数的大小都有一个范围(当然范围越大的占据内存越多):
Range for 16-bit integer: -32768 to 32767
Range for 32-bit integer: –2147483648 to 2147483647
Range for 64-bit integer: –9223372036854775808 to 9223372036854775807
在实际使用中应该根据需求来确定类型,比如说,在这里年龄使用Integer 16就完全够用。
在Type有一个类型是“Binary Data”,这是一个万能的类型,可以储存一切你能想到的可以翻译成二进制的东西,包括图片、PDF、‘小电影’等等。
但是时间花销和空间花销仿佛是一个悖论,这里也是一样,这种方便的操作方式,牺牲了很大的系统花销。也就是说即使你只是想获取他的名字,但是他也会把整个Binary(二进制)加载到缓存中,而这会十分影响用户体验。
十分幸运,Core Data早就想到这个问题了。选择我们的Binary Data类型的属性在右边的属性栏中你会发现一个名为Allows External Storage的选择框,像这样:
当你勾选了 Allows External Storage以后,CoreData将给给每一个值都独立储存,并会生成一个URI作为入口 。
Note:Allows External Storage只有binary data类型拥有,并且,当你勾选了这个选择框以后你就不能使用属性来询问Core Data。
(据说勾选了这个选择框以后还容易导致数据丢失,未验证。)
- 存储非标准类型数据
有一些数据类型在Type一栏中并没有出现,比如UIColor等。
你会如何储存UIColor?像这样(e.g., red: 255, green: 101, blue: 155)?但是,事实上,你可以将UIColor转化为data,然后使用Binary Data类型来进行储存。当你需要读取的时候再将data类型转化为UIColor类型。
Transformable Type
Transformable类型可以储存任何继承自NSCoding的对象,任何你自定义的对象若是继承自NSCoding也可以用Transformable类型来储存。
(2)、使用对象来进行储存以及读取数据
上一篇中我们用key-value来存储、读取数据,look like this:
//Set the name
Test.setValue(name1, forKey: "name")
//Get the name
let name = Test.valueForKey("name")
你的确可以这样操作。但是,这并不意味着你应该这样操作。
key-value并没有充分利用swift类型判断和xcode 自动完成的功能。
最好的方法应该是给每一个Entity创建一个子类,每一个Entity的属性都有自己的类型。
xcode可以自动给Core Data modal中的Entity生成一个类。像这样操作:
选中.xcdatamodeled文件,然后点击Editor->Create NSManagedObject Subclass-> 选中数据模板->选中要生成类的Entity,然后一直点next
在使用我们新创建的对象管理类来进行存醋以及读取等操作之前,我们还有最后一步要做。选择. xcdatamodeled选中Test Entity,打开右边的属性栏,将我们刚才创建的类和Entity连接起来,这看起来似乎和controller和类连接起来有些类似,不过有些不同的就是,这次我们得在类的名字之前添加上ProjectName.。那么在这里就是CoreDataTest2.Test*:
通过对象管理类来进行存储读取有以下两个好处:
-
- 充分利用了xcode和编译器。
- 2.你可以重定义已经存在的方法。
有些方法不被允许继承
上一篇我们已经了解过,在进行Core Data操作时第一步就是获取managedContext(‘暂存器’)。上一篇我们通过 application.delegate来获取‘暂存器’,但是这样操作看起来更像是一个全局变量。这一篇我用类与类之间的属性传递的方法,来获取这个‘暂存器’对象,打开AppDelegate.swift
插入以下代码:
func application(application:UIApplication,didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let viewController = self.window!.rootViewController as ViewController
viewController.managedContext = self.managedObjectContext
return true
}
打开ViewController.swift,添加头文件“import CoreData”,添加一个全局变量。
//这个变量就是我们的‘暂存器’,而这个暂存器在AppDelegate.swift中已经初始化。
var managedContext:NSManagedObjectContext!
给addButton方法添加代码
func insertSampleData() {
//1
let name = self.name.text
let age = self.age.text.toInt()!
let favoriteColor=self.favoriteColor.backgroundColor!
let birthday = self.birthday.date
let avatar = self.Avatar.image!
//2
let entity = NSEntityDescription.entityForName("Test", inManagedObjectContext: managedContext)
let TestObject = Test(entity: entity!, insertIntoManagedObjectContext: managedContext)
//3
TestObject.name=name
TestObject.age=age
TestObject.favoriteColor=favoriteColor
TestObject.birthday=birthday
let avatarData=UIImagePNGRepresentation(avatar)
TestObject.avatar=avatarData
//4
var error: NSError?
if !managedContext.save(&error) {
println("Could not save (error), (error!.userInfo)")
}
解释一下这一段代码:
- //1 获取在屏幕中输入的各种信息。
- //2 新建一个Test对象TestObject,使用初始化方法
init(entity: NSEntityDescription, insertIntoManagedObjectContext context: NSManagedObjectContext?) - //3 给新建的TestObject的变量赋值。
- //4 保存‘暂存器’。
是不是使用对象来进行保存方便许多,是的!
现在运行一下app,填写信息(age一栏请填写整数,否则出错,这里只考虑CoreData相关操作),点击add按钮以后信息被储存,点击show按钮以后跳转到下一界面,但是并没有进行任何操作,现在给showViewController添加代码。
读取数据
打开ShowViewController.swift,在viewDidLoad()中添加以下代码。
override func viewDidLoad() {
super.viewDidLoad()
//1
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext!
//2
let request = NSFetchRequest(entityName:"Test")
var error: NSError? = nil
var results = managedContext.executeFetchRequest(request, error: &error) as! [Test]!
//3
let TestObject=results[results.count-1]
let name = TestObject.name
let age = TestObject.age
let birthday = TestObject.birthday
let favoriteColor:UIColor = TestObject.favoriteColor as! UIColor
let avatar = UIImage(data: TestObject.avatar)
//4
self.Avatar=UIImageView(image: avatar)
self.name.text = name
self.age.text = "(age)"
//格式化输出生日
var fmt=NSDateFormatter()
fmt.dateFormat = "yyyy-MM-dd"
let birthdayString = fmt.stringFromDate(birthday)
self.birthday.text = birthdayString
self.favoriteColor.backgroundColor = favoriteColor
}
代码解释:
- //1 获取‘暂存器’。
- //2 创建请求,发出请求并将结果转化为Test数组。
- //3 取出数组中最后一个Test,也就是我们在界面上只能显示最后一个输入信息,通过对象的属性我们找到了之前输入的信息。
- //4 将取到的信息显示在界面上,其中favoriteColor从Anyobject转化为UIColor,Avatar从NSData转化为UIImage。
好的,这个Demo基本就完成了,我们来运行一下吧:
到了这一篇的最后部分了。
数据验证:
有时候我们对数据的内容有所限制,比如说年龄不能是负的,在coredata中这种验证并不用我们自己来写代码,我们的data model就有这样的功能。
打开我们的data model,选中我们要验证的属性,点开右边的属性辅助栏,如下图所示:
在红色框中我们可以限定最大值 和 最小值。
Note:当我们修改我们的data model以后在运行程序会发生错误,原因就是我们修改了我们的model,最简单的解决方法就是把我们的模拟器reset一下。
好的,接下来我们监测下当输入超出我们的限定之后发生了什么,还记得我们的保存时的error吗,我们将输入的年龄为-1,我们来看一下输出。
既然发生了error,个么问题来了,如何处理error?
var error: NSError?
if !managedContext.save(&error) {
println("Could not save (error), (error!.userInfo)")
if error!.code == NSValidationNumberTooLargeError{
println("值过大")
}
if error!.code == NSValidationNumberTooLargeError {
println("值过小")
}
}
if error!.code == NSValidationNumberTooLargeError
if error!.code == NSValidationNumberTooLargeError
这两句判断语句能准确的告诉我们到底是发生了什么错误,根据不同错误以及实际情况来进行处理,灵活机动。
这一篇内容大体就是这样了,主要讲了通过对象来进行存储以及读取,而这样操作的好处就是更加方便,充分利用了swift以及xcode的特性,也不容易出错。
源代码已上传Github:https://github.com/superxlx/CoreDataTest2
好了,在下一篇再见。