最近项目写 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
|
let privateContext = NSManagedObjectContext( concurrencyType: .PrivateQueueConcurrencyType) privateContext.persistentStoreCoordinator = coreDataStack.context.persistentStoreCoordinator
privateContext.performBlock { () -> Void in 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)
let fileHandle: NSFileHandle? do { fileHandle = try NSFileHandle(forWritingToURL: exportFileURL) } catch { let nserror = error as NSError print("ERROR: \(nserror)") fileHandle = nil }
if let fileHandle = fileHandle { for object in results { let journalEntry = object as! JournalEntry
fileHandle.seekToEndOfFile() let csvData = journalEntry.csv().dataUsingEncoding( NSUTF8StringEncoding, allowLossyConversion: false) fileHandle.writeData(csvData!) }
fileHandle.closeFile()
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() }) }
}
|
9. 参考资料