ios10 swift3中的coreData新使用

iOS 10 中的 NSPersistentContainer

Xcode 8 已經(jīng)面世了京闰,如果你還沒有嘗試過這個測試版本经伙,你將會發(fā)現(xiàn)各種新東西贰健。這里有 Swift 3 [主要的更新]辑鲤,有新的框架呻粹,比如 [SiriKit]和一些對現(xiàn)存特性的增強改進,比如 [notifications]昼扛。 我們也接收以 NSPersistentContainer
形式的簡化版的 Core Data stack
寸齐,它為我們做了大部分的準備工作。它值得我們?nèi)L試么?讓我們開始深入挖掘這些新特性吧访忿。
iOS 10
之前的 Core Data stack

多年來,在嘗試了很多種 Core Data stack
之后斯稳,我們選定了兩個簡單的 stack
海铆,融合成一個使用。讓我們仔細看一下這些關(guān)鍵組件并開始連接使用他們挣惰。完整版本的 Github
鏈接在引用中能找到卧斟。代碼已經(jīng)適配到 Swift 3
和 Xcode 8

final class CoreDataStack {
    static let sharedStack = CoreDataStack()
    var errorHandler: (Error) -> Void = {_ in }

    private init() {
    #1
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(CoreDataStack.mainContextChanged(notification:)),
                                               name: .NSManagedObjectContextDidSave,
                                               object: self.managedObjectContext)
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(CoreDataStack.bgContextChanged(notification:)),
                                               name: .NSManagedObjectContextDidSave,
                                               object: self.backgroundManagedObjectContext)

    }

    deinit {
        NotificationCenter.default.removeObserver(self)
    }

    #2
    lazy var applicationDocumentsDirectory: NSURL = {
        let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        return urls[urls.count-1] as NSURL
    }()

    #3
    lazy var managedObjectModel: NSManagedObjectModel = {
        let modelURL = Bundle.main.url(forResource: "DataModel", withExtension: "momd")!
        return NSManagedObjectModel(contentsOf: modelURL)!
    }()

    #4
    lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
        let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
        let url = self.applicationDocumentsDirectory.appendingPathComponent("DataModel.sqlite")
        do {
            try coordinator.addPersistentStore(ofType: NSSQLiteStoreType,
                                               configurationName: nil,
                                               at: url,
                                               options: [NSMigratePersistentStoresAutomaticallyOption: true,
                                                         NSInferMappingModelAutomaticallyOption: true])
            } catch {
                // Report any error we got.
                NSLog("CoreData error \(error), \(error._userInfo)")
                self.errorHandler(error)
            }
        return coordinator
    }()

    #5
    lazy var backgroundManagedObjectContext: NSManagedObjectContext = {
        let coordinator = self.persistentStoreCoordinator
        var privateManagedObjectContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
        privateManagedObjectContext.persistentStoreCoordinator = coordinator
        return privateManagedObjectContext
    }()

    #6
    lazy var managedObjectContext: NSManagedObjectContext = {
        let coordinator = self.persistentStoreCoordinator
        var mainManagedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
        mainManagedObjectContext.persistentStoreCoordinator = coordinator
        return mainManagedObjectContext
    }()

    #7
    @objc func mainContextChanged(notification: NSNotification) {
        backgroundManagedObjectContext.perform { [unowned self] in
            self.backgroundManagedObjectContext.mergeChanges(fromContextDidSave: notification as Notification)
        }
    }

    @objc func bgContextChanged(notification: NSNotification) {
        managedObjectContext.perform{ [unowned self] in
            self.managedObjectContext.mergeChanges(fromContextDidSave: notification as Notification)
        }
    }
}

上面是啥憎茂?且容我慢慢道來珍语。
#1
在初始化的時候,我們訂閱了從主線程和后臺線程 NSMagedObjectContext
發(fā)送來的通知竖幔。
#2
獲取文檔路徑 NSURL的 getter板乙。NSPersistentStoreCoordinator
使用它在給定的位置創(chuàng)建 NSPersistentStore。
#3
和文件目錄相似拳氢,他獲得 NSManagedObjectModel 的 getter
方法募逞,用它來初始化有我們模型的 NSPersistentStoreCoordinator。
#4
這就是這些神奇的代碼干的事情馋评。首先放接,我們創(chuàng)建有模型的 NSPersistentStoreCoordinator。之后留特,我們獲取我們文檔目錄的 url纠脾。最后,我們在這些文檔目錄內(nèi)為某些類型的 NSPersistentStoreCoordinator
增加一個持久化的存儲蜕青。
#5
我們在一個私有隊列里創(chuàng)建一個’后臺’ NSManagedObjectContext
并且把它綁定到 NSPersistentStoreCoordinator
苟蹈。這個 context被用于執(zhí)行同步和寫操作。
#6
我們在主隊列中創(chuàng)建一個’視圖’ NSManagedObjectContext
并且把它綁定到我們的 NSPersistentStoreCoordinator
右核。這個 context
被用于獲取顯示在 UI
上的數(shù)據(jù)汉操。
#7
這個 stack使用了穩(wěn)定、成熟的融合過的 contexts蒙兰,它被保存的 notifications 驅(qū)動磷瘤。在這些方法中,我們執(zhí)行這個融合搜变。NSPersistentContainer
簡介
iOS 10 給我們提供了 NSPersistentContainer采缚。它意圖簡化代碼并且為我們解決負擔。它能做到么挠他?讓我展示給你我們基于 NSPersistentContainer
重建 CoreData stack 扳抽。
一個完整的例子:

final class CoreDataStack {

    static let shared = CoreDataStack()
    var errorHandler: (Error) -> Void = {_ in }

    #1
    lazy var persistentContainer: NSPersistentContainer = {
        let container = NSPersistentContainer(name: "DataModel")
        container.loadPersistentStores(completionHandler: { [weak self](storeDescription, error) in
            if let error = error {
                NSLog("CoreData error \(error), \(error._userInfo)")
                self?.errorHandler(error)
            }
            })
        return container
    }()

    #2
    lazy var viewContext: NSManagedObjectContext = {
        return self.persistentContainer.viewContext
    }()

    #3
    // Optional
    lazy var backgroundContext: NSManagedObjectContext = {
        return self.persistentContainer.newBackgroundContext()
    }()

    #4
    func performForegroundTask(_ block: @escaping (NSManagedObjectContext) -> Void) {
        self.viewContext.perform {
            block(self.viewContext)
        }
    }

    #5
    func performBackgroundTask(_ block: @escaping (NSManagedObjectContext) -> Void) {
        self.persistentContainer.performBackgroundTask(block)
    }
}

實際上這個更簡短。但是之前版本的代碼發(fā)生了什么?
簡單的答案是贸呢,NSPersistentContainer
已可以為我們代勞镰烧。對于一個博客文章的解釋,這肯定不夠 ?? 楞陷。還是容我慢慢道來怔鳖。
#1
這里,我們能看到 NSPersistentContainer的能力固蛾。它完成了之前 stack
內(nèi)#2, #3, #4, #5, #6 的工作结执,并一定程度上把我們從 #1 和 #7 中的工作中解放出來。怎么做到的艾凯?
首先献幔,它通過一個名字來初始化,這個名字被用于在文檔目錄中查找一個模型并且用相同的名字創(chuàng)建一個存儲器趾诗。這是一個快捷初始器蜡感。你也可以使用完整的版本,手動地傳遞你的模型恃泪。

public init(name:String,managedObjectModel model:NSManagedObjectModel)

之后铸敏,在調(diào)用 loadPersistentStores
方法之前,你還有時間來進一步配置你的容器悟泵,例如杈笔,使用 NSPersistentStoreDescription
。我們使用一個默認的 SQLite數(shù)據(jù)庫糕非,所以我們裝載自己的永久存儲器并且確保錯誤處理蒙具。
#2
實際上這只是一個封裝器。已經(jīng)通過 NSPersistentContainer
為我們創(chuàng)建了 viewContext朽肥。而且禁筏,它已經(jīng)被配置成可以接收從其他的 contexts來的保存通知。引用自 Apple公司:這個被管理的 context對象與主隊列有關(guān)衡招。(只讀)… 這個 context是被配置成可持續(xù)的篱昔,并且從其他 contexts處理保存的通知。

#3NSpersistentContainer
也給予了我們一個工廠方法始腾,它用來創(chuàng)建多個私有隊列的 contexts
州刽。我們?yōu)榱藦?fù)雜的同步目的,在這里僅使用一個浪箭,常見的后臺 context
穗椅。由工廠方法創(chuàng)建出的 Contexts
也被設(shè)定成可自動地接收和處理 NSManagedObjectContextDidSave
的廣播消息。 這是可選項奶栖。
#4NSPersistentContainer
在后臺(詳情可見 #5)為運行 Core Data stack
暴露了一個方法匹表。我們非常喜歡這個 API
的命名门坷,所以我們也為 viewContext
創(chuàng)建了類似的封裝器。
#5
正如上文提到的袍镀,這僅是一個有關(guān) performBackgroundTask
方法的封裝器默蚌,它是 NSPersistentContainer
中的一個方法。每一次它調(diào)用一個新的 context
苇羡, parivateQueueConcurrencyType
也被創(chuàng)建绸吸。
注意: 我們已討論了大部分 NSPersistentContainer
的特性,但是你也可以查看[參考資料]宣虾,去查閱完整的內(nèi)容。
如果 NSPersistentContinainer
對我來說還是太龐大温数?
有一些可選項绣硝。
首先,確保查閱了完整的參考資料撑刺,并且在尋找你所需要的屬性或者方法鹉胖。我們已經(jīng)涵蓋了兩個初始化器,一個僅需要字符串名和完整采用 NSManagedObjectModel
的快捷方法够傍。
之后甫菠,你可以調(diào)查擴展或者子類。舉個例子冕屯,在我們其中一個項目中寂诱,我們在核心程序和擴展程序之間共享了一個 Core Data stack
。它不得不落地在一個 App 共享組群空間中安聘,并且 NSPersistentContainer
默認的文檔目錄已經(jīng)不再為我們所用痰洒。
幸運的是,通過一個輕量的子類 NSPersistentContainer
浴韭,我們又滿血復(fù)活了丘喻,并且能繼續(xù)使用那些容器類帶來的好處。

struct CoreDataServiceConsts {
    static let applicationGroupIdentifier = "group.com.identifier.app-name"
}

final class PersistentContainer: NSPersistentContainer {
    internal override class func defaultDirectoryURL() -> URL {
        var url = super.defaultDirectoryURL()
        if let newURL =
            FileManager.default.containerURL(
                forSecurityApplicationGroupIdentifier: CoreDataServiceConsts.applicationGroupIdentifier) {
            url = newURL
        }
        return url
    }
}

總結(jié) & 參考文獻

我希望你們喜歡這篇有關(guān) NSPersistentContainer
的簡短精干的文章念颈,并且我們也希望看到你們是如何通過這些在 Core Data
框架上的改進來演進你們的 Core Data stack泉粉。
稍等一下… 啊榴芳?還有其他的改變么嗡靡?
是的,當然有窟感。最佳的方法是通過 Apple公司的官方推文 ‘Core Data 在 iOS 10 上的新特性’叽躯。這些改變從并發(fā)、context版本肌括、請求獲取点骑、自動融合來自父 context 變化等開始酣难,以在 macOS 10.12中的 NSFetchResultsController結(jié)束。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末黑滴,一起剝皮案震驚了整個濱河市憨募,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌袁辈,老刑警劉巖菜谣,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異晚缩,居然都是意外死亡尾膊,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進店門荞彼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來冈敛,“玉大人,你說我怎么就攤上這事鸣皂∽デ矗” “怎么了?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵寞缝,是天一觀的道長癌压。 經(jīng)常有香客問我,道長荆陆,這世上最難降的妖魔是什么滩届? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮被啼,結(jié)果婚禮上丐吓,老公的妹妹穿的比我還像新娘。我一直安慰自己趟据,他們只是感情好券犁,可當我...
    茶點故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著汹碱,像睡著了一般粘衬。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上咳促,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天稚新,我揣著相機與錄音,去河邊找鬼跪腹。 笑死褂删,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的冲茸。 我是一名探鬼主播屯阀,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼缅帘,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了难衰?” 一聲冷哼從身側(cè)響起钦无,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎盖袭,沒想到半個月后失暂,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡鳄虱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年弟塞,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拙已。...
    茶點故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡决记,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出悠栓,到底是詐尸還是另有隱情霉涨,我是刑警寧澤按价,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布惭适,位于F島的核電站,受9級特大地震影響楼镐,放射性物質(zhì)發(fā)生泄漏癞志。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一框产、第九天 我趴在偏房一處隱蔽的房頂上張望凄杯。 院中可真熱鬧,春花似錦秉宿、人聲如沸戒突。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽膊存。三九已至,卻和暖如春忱叭,著一層夾襖步出監(jiān)牢的瞬間隔崎,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工韵丑, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留爵卒,地道東北人。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓撵彻,卻偏偏與公主長得像钓株,于是被迫代替她去往敵國和親实牡。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,440評論 2 348

推薦閱讀更多精彩內(nèi)容