簡體   English   中英

Swift 4中的核心數據並發問題

[英]Core Data Concurrency issue in Swift 4

當線程不是主線程時,從核心數據中檢索NSManagedObj時會遇到核心數據並發性問題。

我試用了Swift 4的核心數據,當代碼很簡單時,最初一切都運行良好。 但是,當我為核心數據添加更多代碼並增加了復雜性時,偶然遇到一個錯誤,並發現了核心數據的並發問題。

我遇到的錯誤是

CoreData: error: NULL _cd_rawData but the object is not being turned into a fault

我搜索了一些嘗試解決問題的方法,方法是添加以下行來處理涉及后台線程而不是主線程的檢索核心數據操作。

appDelegate.persistentContainer.performBackgroundTask{....}

但是,它仍然有問題。

首先,這是檢索功能

func retrieve() -> MasterSlave?{
    let appDelegate = UIApplication.shared.delegate as! AppDelegate
    let context = appDelegate.persistentContainer.viewContext
    var count = 0
    var masterSlave: MasterSlave?
    DispatchQueue.main.async {
        print("master slave receive main thread is", Thread.isMainThread)
        count = appDelegate.retrieve("MasterSlave", predicate: nil, sort: nil, limit: nil)!.count
        if count > 0 {
            masterSlave = appDelegate.retrieve("MasterSlave", predicate: nil, sort: nil, limit: nil)?.first! as? MasterSlave
        }
    }

    appDelegate.persistentContainer.performBackgroundTask{ context in
        print("master slave receive main thread is", Thread.isMainThread)
        let ms = appDelegate.retrieve("MasterSlave", predicate: nil, sort: nil, limit: nil)!
        print("ms obj retreived is", ms, "count is ", ms.count)
        count = appDelegate.retrieve("MasterSlave", predicate: nil, sort: nil, limit: nil)!.count
        if count > 0 {
            masterSlave = appDelegate.retrieve("MasterSlave", predicate: nil, sort: nil, limit: nil)?.first! as? MasterSlave
        }
    }
    return masterSlave
}

這是appDelegate中的檢索功能

    func retrieve(_ myEntityName:String, predicate:String?, sort:[[String:Bool]]?, limit:Int?) -> [NSManagedObject]? {
    let myContext = persistentContainer.viewContext
    let request = NSFetchRequest<NSFetchRequestResult>(entityName: myEntityName)

    // predicate
    if let myPredicate = predicate {
        request.predicate = NSPredicate(format: myPredicate)
    }

    // sort
    if let mySort = sort {
        var sortArr :[NSSortDescriptor] = []
        for sortCond in mySort {
            for (k, v) in sortCond {
                sortArr.append(NSSortDescriptor(key: k, ascending: v))
            }
        }

        request.sortDescriptors = sortArr
    }

    // limit
    if let limitNumber = limit {
        request.fetchLimit = limitNumber
    }


    do {
        return try myContext.fetch(request) as? [NSManagedObject]

    } catch {
        fatalError("\(error)")
    }

    return nil
}

現在,發現了一些奇怪的東西:MasterSlave實體應該只有一個對象,我之前保存過一次。 因此,當它運行時, let ms = appDelegate.retrieve("MasterSlave", predicate: nil, sort: nil, limit: nil)! ,預計將檢索以下數據

data: {
encryptKey = nil;
hasMaster = 0;
lock = 0;
master = 0;
password = nil;
slave = 1;
}

但是它提供了以下內容

[<MasterSlave: 0x600000280190> (entity: MasterSlave; id: 0xd000000000040004 <x-coredata://5FF8490C-B1C5-417D-BA3E-2F30B5D2BAD6/MasterSlave/p1> ; data: <fault>), 
 <MasterSlave: 0x600000280140> (entity: MasterSlave; id: 0xd000000000080004 <x-coredata://5FF8490C-B1C5-417D-BA3E-2F30B5D2BAD6/MasterSlave/p2> ; data: <fault>),
 <MasterSlave: 0x60000009fd60> (entity: MasterSlave; id: 0xd0000000000c0004 <x-coredata://5FF8490C-B1C5-417D-BA3E-2F30B5D2BAD6/MasterSlave/p3> ; data: <fault>),
 <MasterSlave: 0x6000002800f0> (entity: MasterSlave; id: 0xd000000000100004 <x-coredata://5FF8490C-B1C5-417D-BA3E-2F30B5D2BAD6/MasterSlave/p4> ; data: <fault>), 
 <MasterSlave: 0x6000002800a0> (entity: MasterSlave; id: 0xd000000000140004 <x-coredata://5FF8490C-B1C5-417D-BA3E-2F30B5D2BAD6/MasterSlave/p5> ; data: <fault>), 
 <MasterSlave: 0x600000280050> (entity: MasterSlave; id: 0xd000000000180004 <x-coredata://5FF8490C-B1C5-417D-BA3E-2F30B5D2BAD6/MasterSlave/p6> ; data: <fault>), 
 <MasterSlave: 0x600000280000> (entity: MasterSlave; id: 0xd0000000001c0004 <x-coredata://5FF8490C-B1C5-417D-BA3E-2F30B5D2BAD6/MasterSlave/p7> ; data: <fault>), 
 <MasterSlave: 0x60000009ff90> (entity: MasterSlave; id: 0xd000000000200004 <x-coredata://5FF8490C-B1C5-417D-BA3E-2F30B5D2BAD6/MasterSlave/p8> ; data: <fault>), 
 <MasterSlave: 0x60000009ff40> (entity: MasterSlave; id: 0xd000000000240004 <x-coredata://5FF8490C-B1C5-417D-BA3E-2F30B5D2BAD6/MasterSlave/p9> ; data: <fault>), 
 <MasterSlave: 0x60000009fef0> (entity: MasterSlave; id: 0xd000000000280004 <x-coredata://5FF8490C-B1C5-417D-BA3E-2F30B5D2BAD6/MasterSlave/p10> ; data: <fault>), 
 <MasterSlave: 0x60000009fea0> (entity: MasterSlave; id: 0xd0000000002c0004 <x-coredata://5FF8490C-B1C5-417D-BA3E-2F30B5D2BAD6/MasterSlave/p11> ; data: <fault>), 
 <MasterSlave: 0x60000009fe50> (entity: MasterSlave; id: 0xd000000000300004 <x-coredata://5FF8490C-B1C5-417D-BA3E-2F30B5D2BAD6/MasterSlave/p12> ; data: <fault>), 
 <MasterSlave: 0x60000009fe00> (entity: MasterSlave; id: 0xd000000000340004 <x-coredata://5FF8490C-B1C5-417D-BA3E-2F30B5D2BAD6/MasterSlave/p13> ; data: <fault>), 
 <MasterSlave: 0x60000009fdb0> (entity: MasterSlave; id: 0xd000000000380004 <x-coredata://5FF8490C-B1C5-417D-BA3E-2F30B5D2BAD6/MasterSlave/p14> ; data: <fault>), 
 <MasterSlave: 0x60000009fcc0> (entity: MasterSlave; id: 0xd0000000003c0004 <x-coredata://5FF8490C-B1C5-417D-BA3E-2F30B5D2BAD6/MasterSlave/p15> ; data: <fault>), 
 <MasterSlave: 0x600000280230> (entity: MasterSlave; id: 0xd000000000400004 <x-coredata://5FF8490C-B1C5-417D-BA3E-2F30B5D2BAD6/MasterSlave/p16> ; data: <fault>), 
 <MasterSlave: 0x60000009fa90> (entity: MasterSlave; id: 0xd000000000440004 <x-coredata://5FF8490C-B1C5-417D-BA3E-2F30B5D2BAD6/MasterSlave/p17> ; data: {
encryptKey = nil;
hasMaster = 0;
lock = 0;
master = 0;
password = nil;
slave = 1;
})]

當我停止應用程序並再次部署時,masterSlave的[NSManagedObject]的數量變為18而不是17。

我不明白為什么每次構建部署時檢索到的[NSManagedObject]的計數都會增加。 另外,當它在主線程中時,一切工作正常。

有人有什么想法嗎?

核心數據不是線程安全的。 無論是閱讀還是寫作。 每個上下文都有一個(只有一個)線程可以安全訪問。 對於viewContext,它是主線程。 對於由performBackgroundTask創建的上下文,只能在該塊內部訪問它。 對於上下文和與上下文關聯的所有ManagedObjects都是如此。

從核心數據中獲取數據的retrieve方法,應該接受上下文作為參數。 從主要使用它們時,應將其傳遞給viewContext; 當它來自performBackgroundTask時,應該使用傳遞給該塊的上下文。

您不能將對象傳入到performBackgroundTask塊中或從中傳出。 因此,您不應從performBackgroundTask內部訪問任何主線程對象。 相反,您應該保存對象的objectID並使用它進行重新提取。 您不能將ManagedObjects傳遞到塊的外部,但是要傳遞它們內部的數據。

您在retrieve()使用塊是毫無意義的。 該方法在執行任何一個塊之前返回,並且masterSlave始終為零。 通常,您應該從已知已經在主線程上的方法中獲取主線程。 使用DispatchQueue.main.async進行提取,然后嘗試將數據發送回后台線程絕不是一個好主意-通常,您希望在主線程上顯示該信息。

如果要保證只有一個實體實例,則應在創建該對象之前對其進行提取。 似乎每次應用啟動時您都在創建一個新對象。 您尚未共享該代碼,因此我無法進一步評論。

如果您希望只有不到一百個實體閱讀僅在viewContext上的文字,則這不是不合理的設置。 如果是這種情況,我建議您這樣做,因為它可以解決您的大多數問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM