iOS core Data 詳解-多線程

在之前的介紹中,我們操作core data都是在主線程的,但是有的時候,我們對core data的操作可能會消耗很長的時間,比如類似微博,在程序啓動的時候會加載之前存儲在數據庫中的數據,如果都在主線程操作的話,那麼將會照成主線程堵塞,給用戶不好的體驗,這是我們就需要使用Core Data的多線程特性!

多線程解決方案

core data不是線程安全的,所以我們不能跨線程去操作它,如果涉及多線程的操作,最好的方式是每個線程都是不同的NSManagerObjectContext,對於線程間需要傳遞ManagedObject的,傳遞ManagedObject ID,通過objectWithID或者existingObjectWithID 來獲取, 對於持久化存儲協調器(NSPersistentStoreCoordinator)來說,可以多個線程共享一個NSPersistentStoreCoordinator。

使用Notification

Notification消息類型

  1. NSManagedObjectContextObjectsDidChangeNotification:當manager object對象中的屬性值發生變化的時候會觸發這個通知(當manager object以fetch結果接入到context的時候並不會觸發該通知),通知的對象是對應的manager context,主要由以下幾個值:NSInsertedObjectsKey, NSUpdatedObjectsKey, and NSDeletedObjectsKey。
  2. NSManagedObjectContextDidSaveNotification:當managed object context完成保存操作時觸發該通知。
  3. NSManagedObjectContextWillSaveNotification:當managed object context即將要執行保存操作時觸發該通知。

    使用

    一種比較好的iOS模式就是使用一個NSPersistentStoreCoordinator,以及兩個獨立的Contexts,一個context負責主線程與UI協作,一個context在後臺負責耗時的處理。這樣做的好處就是兩個context共享一個持久化存儲緩存,而且這麼做互斥鎖只需要在sqlite級別即可。設置當主線程只讀的時候,都不需要鎖。
    首先再主線程增加監聽事件:

NSNotificationCenter.defaultCenter().addObserver(self, selector: "didSaveNotification:", name: NSManagedObjectContextObjectsDidChangeNotification, object: nil)

func didSaveNotification(notification: NSNotification) {
        if  let currentContext = notification.object {
            let context = currentContext as! NSManagedObjectContext
            if context == appDelegate().managedObjectContext {
                return
            }
            if context.persistentStoreCoordinator != appDelegate().persistentStoreCoordinator {
                return
            }

            dispatch_async(dispatch_get_main_queue(), { () -> Void in
                self.appDelegate().managedObjectContext.mergeChangesFromContextDidSaveNotification(notification)
            })
        }
    }

然後我們把對core data的操作放入線程中進行操作,再線程中創建context的時候,指定persistentStoreCoordinator和主線程的一致:

 func addDataUseObjc() {

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { () -> Void in
            let context = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
            context.persistentStoreCoordinator = self.appDelegate().persistentStoreCoordinator

            let author = NSEntityDescription.insertNewObjectForEntityForName("Author", inManagedObjectContext: context) as! Author
            author.name = "jamy001"
            author.age = NSNumber(integer: 25)

            let book = NSEntityDescription.insertNewObjectForEntityForName("Book", inManagedObjectContext: context) as! Book
            book.bookName = "很好一本書"

            author.book = book

            if context.hasChanges {
                do {
                    try context.save()
                } catch {
                    let nserror = error as NSError
                    NSLog("Unresolved error \(nserror), \(nserror.userInfo)")
                    abort()
                }
            }
        }
    }

使用child/parent context

ChildContext和ParentContext是相互獨立的。只有當ChildContext中調用Save了以後,纔會把這段時間來Context的變化提交到ParentContext中,ChildContext並不會直接提交到NSPersistentStoreCoordinator中, parentContext就相當於它的NSPersistentStoreCoordinator。
child/parent context的結構圖如下:
這裏寫圖片描述

通常主線程context使用NSMainQueueConcurrencyType,其他線程childContext使用NSPrivateQueueConcurrencyType. child和parent的特點是要用Block進行操作,performBlock,或者performBlockAndWait,保證線程安全。這兩個函數的區別是performBlock不會阻塞運行的線程,相當於異步操作,performBlockAndWait會阻塞運行線程,相當於同步操作,還有一點需要注意:childContext的type儘量不能使用NSConfinementConcurrencyType,因爲使用這種的parent必須要是persistent store coordinator,不能爲parent context!

 func addDataUseChild() {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { () -> Void in
            let context = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
            context.parentContext = self.appDelegate().managedObjectContext

            let author = NSEntityDescription.insertNewObjectForEntityForName("Author", inManagedObjectContext: context) as! Author
            author.name = "jamy004"
            author.age = NSNumber(integer: 26)

            let book = NSEntityDescription.insertNewObjectForEntityForName("Book", inManagedObjectContext: context) as! Book
            book.bookName = "很好一本書12"

            author.book = book

            context.performBlockAndWait({ () -> Void in
                if context.hasChanges {
                    do {
                        try context.save()
                    } catch {
                        let nserror = error as NSError
                        NSLog("Unresolved error \(nserror), \(nserror.userInfo)")
                        abort()
                    }
                }
            })
            self.fetchData()
        }
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章