Core Data - Notes

最近项目写 Core Data 感觉之前的看过零碎的知识忘记的差不多了,又遇到异步处理的问题。重新看了一些资料,总结了一些要点如下。

1.

Core Data 是一个 对象图管理和存储框架。简单明确的属性和关系以及获取,都已封装好。不管底层数据库的实现,开发者只需关心数据和获取就行了。

2.

图形化编辑器:xcdatamodel

managed object model :

  • 属性支持 NSData:Binary Data 和符合 NSCoding protocol 的类型:Transformable
  • 关系建议采取 inverse
  • 关系一对多和一对一,其中有有序和无序的一对多的关系,分别为 NSSet 和 NSOrderedSet,具体可以参考这样文章

Core Data and Swift: Relationships and More Fetching :
http://code.tutsplus.com/tutorials/core-data-and-swift-relationships-and-more-fetching--cms-25070

3.

Core Data Stack 涉及四个类:

  • NSManagedObjectModel
  • NSPersistentStore
  • NSPersistentStoreCoordinator
  • NSManagedObjectContext

4.

NSManagedObjectContext:

  • 内存寄存器来处理 managed objects
  • 记得 save()
  • 掌管 managed objects 生命周期包括创建和获取
  • managed object 不能独立于 context 存在
  • context 具有领域性,一旦一个 managed object 被管理在一个 context ,将会在其整个生命周期绑定该 context
  • 支持多个 context
  • context 不是线程安全的

5.

如何配置 Core Data Stack:

其中 lazy、try catch 等技术细节不用多解释,后面在介绍多个 context 和异步处理的线程安全问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import CoreData

class CoreDataStack {

let modelName = "Dog Walk"

lazy var context: NSManagedObjectContext = {

var managedObjectContext = NSManagedObjectContext(
concurrencyType: .MainQueueConcurrencyType)

managedObjectContext.persistentStoreCoordinator = self.psc
return managedObjectContext
}()

private lazy var psc: NSPersistentStoreCoordinator = {

let coordinator = NSPersistentStoreCoordinator(
managedObjectModel: self.managedObjectModel)

let url = self.applicationDocumentsDirectory
.URLByAppendingPathComponent(self.modelName)

do {
let options =
[NSMigratePersistentStoresAutomaticallyOption : true]

try coordinator.addPersistentStoreWithType(
NSSQLiteStoreType, configuration: nil, URL: url,
options: options)
} catch {
print("Error adding persistent store.")
}

return coordinator
}()

private lazy var managedObjectModel: NSManagedObjectModel = {

let modelURL = NSBundle.mainBundle()
.URLForResource(self.modelName,
withExtension: "momd")!
return NSManagedObjectModel(contentsOfURL: modelURL)!
}()

private lazy var applicationDocumentsDirectory: NSURL = {
let urls = NSFileManager.defaultManager().URLsForDirectory(
.DocumentDirectory, inDomains: .UserDomainMask)
return urls[urls.count-1]
}()

func saveContext () {
if context.hasChanges {
do {
try context.save()
} catch let error as NSError {
print("Error: \(error.localizedDescription)")
abort()
}
}
}
}

6.

Fetch

  • NSManagedObjectResultType: 默认值,返回 managed objects
  • NSCountResultType: 返回 count
  • NSDictionaryResultType: 返回一个计算后值,如 sum。详细用法可以看文档 NSExpression
  • NSManagedObjectIDResultType:

从性能优化的角度,可以考虑时候后面的几个类型。
iOS8异步fetch:NSAsynchronousFetchRequest、批量更新/删除属性

7.

fetched results controller 可以帮助我们处理 core data 和 table view datasource,可以简单的看成专用的 datasource。

记得添加 cacheName

8.

后台处理使用 context PrivateQueueConcurrencyType,默认使用 MainQueueConcurrencyType,尤其设计 UI。

可以使用 child context,先保存 child context 至 内存寄存器,一直到 parent context 保存后,才会保存至硬盘。

这里就涉及一个好的实践:有多个 context 总是调用 performBlock 来保证安全。

下面是一个 private context 后台处理,回到主线程的实践:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68


// 1
let privateContext = NSManagedObjectContext(
concurrencyType: .PrivateQueueConcurrencyType)
privateContext.persistentStoreCoordinator =
coreDataStack.context.persistentStoreCoordinator

// 2
privateContext.performBlock { () -> Void in
// 3
let results: [AnyObject]
do {
results = try self.coreDataStack.context
.executeFetchRequest(self.surfJournalFetchRequest())
} catch {
let nserror = error as NSError
print("ERROR: \(nserror)")
results = []
}

let exportFilePath =
NSTemporaryDirectory() + "export.csv"
let exportFileURL = NSURL(fileURLWithPath: exportFilePath)
NSFileManager.defaultManager().createFileAtPath(
exportFilePath, contents: NSData(), attributes: nil)

// 3
let fileHandle: NSFileHandle?
do {
fileHandle = try NSFileHandle(forWritingToURL: exportFileURL)
} catch {
let nserror = error as NSError
print("ERROR: \(nserror)")
fileHandle = nil
}

if let fileHandle = fileHandle {
// 4
for object in results {
let journalEntry = object as! JournalEntry

fileHandle.seekToEndOfFile()
let csvData = journalEntry.csv().dataUsingEncoding(
NSUTF8StringEncoding, allowLossyConversion: false)
fileHandle.writeData(csvData!)
}

// 5
fileHandle.closeFile()

// 4
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.navigationItem.leftBarButtonItem =
self.exportBarButtonItem()
print("Export Path: \(exportFilePath)")
self.showExportFinishedAlertView(exportFilePath)
})
} else {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.navigationItem.leftBarButtonItem =
self.exportBarButtonItem()
})
}

} // 5 Closing brace for performBlock()


9. 参考资料