批量导入Core data中具有关系的大集

iOS*_*eek 5 import json core-data ios swift

我正在尝试导入大约 80k 对象的大型数据集。我正在尝试效仿苹果的例子

\n\n

我有两个问题:

\n\n
    \n
  1. 代码示例中有一条注释:
  2. \n
\n\n
 // taskContext.performAndWait runs on the URLSession\'s delegate queue\n // so it won\xe2\x80\x99t block the main thread.\n
Run Code Online (Sandbox Code Playgroud)\n\n

但就我而言,我没有使用 URLSession 来获取 JSON。该文件与应用程序捆绑在一起。在这种情况下,如何确保导入不会阻塞主线程。我应该创建一个自定义队列吗?有什么例子吗?

\n\n
    \n
  1. 在示例中,它只是导入实体数组。但就我而言,我只需要导入一个具有 70k 个对象且与多个对象相关的实体。

    \n\n

    在此输入图像描述

  2. \n
\n\n

所以我想要实现的是:

\n\n
    \n
  • 如果有,ContactBook请不要导入任何内容,因为我们已经导入了 JSON。
  • \n
  • 如果没有,ContactBook则创建一个并将所有 70kContact对象导入contactsContactBook. 这应该像示例中那样批量发生,并且不应该阻塞 UI。
  • \n
\n\n

我尝试过的:

\n\n
private func insertContactbookIfNeeded() {\n    let fetch: NSFetchRequest<Contactbook> = ContactBook.fetchRequest()\n    let contactBookCount = (try? context.count(for: fetch)) ?? 0\n\n    if contactBookCount > 0 {\n        return\n    }\n\n    let contacts = Bundle.main.decode([ContactJSON].self, from: "contacts.json")\n\n    // Process records in batches to avoid a high memory footprint.\n    let batchSize = 256\n    let count = contacts.count\n\n    // Determine the total number of batches.\n    var numBatches = count / batchSize\n    numBatches += count % batchSize > 0 ? 1 : 0\n\n    for batchNumber in 0 ..< numBatches {\n\n        // Determine the range for this batch.\n        let batchStart = batchNumber * batchSize\n        let batchEnd = batchStart + min(batchSize, count - batchNumber * batchSize)\n        let range = batchStart..<batchEnd\n\n        // Create a batch for this range from the decoded JSON.\n        let contactsBatch = Array(contacts[range])\n\n        // Stop the entire import if any batch is unsuccessful.\n        if !importOneBatch(contactsBatch) {\n            assertionFailure("Could not import batch number \\(batchNumber) range \\(range)")\n            return\n        }\n    }\n}\n\nprivate func importOneBatch(_ contactsBatch: [ContactJSON]) -> Bool {\n\n    var success = false\n\n    // Create a private queue context.\n    let taskContext = container.newBackgroundContext()\n    taskContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy\n\n    // NOT TRUE IN MY CASE: (Any suggestion ??)\n    // taskContext.performAndWait runs on the URLSession\'s delegate queue\n    // so it won\xe2\x80\x99t block the main thread. \n\n    print("isMainThread: \\(Thread.isMainThread)") // prints true\n\n    taskContext.performAndWait {\n        let fetchRequest: NSFetchRequest<ContactBook> = ContactBook.fetchRequest()\n        fetchRequest.returnsObjectsAsFaults = true\n        fetchRequest.includesSubentities = false\n\n        let contactBookCount = (try? taskContext.count(for: fetchRequest)) ?? 0\n\n        var contactBook: ContactBook?\n\n        if contactBookCount > 0 {\n            do {\n                contactBook = try taskContext.fetch(fetchRequest).first\n            } catch let error as NSError {\n                assertionFailure("can\'t fetch the contactBook \\(error)")\n            }\n        } else {\n            contactBook = ContactBook(context: taskContext)\n        }\n\n        guard let book = contactBook else {\n            assertionFailure("Could not fetch the contactBook")\n            return\n        }\n\n        // Create a new record for each contact in the batch.\n        for contactJSON in contactsBatch {\n\n            // Create a Contact managed object on the private queue context.\n            let contact = Contact(context: taskContext)\n            // Populate the Contact\'s properties using the raw data.\n            contact.name = contactJSON.name\n            contact.subContacts = NSSet(array: contactJSON.subContacts { subC -> Contact in\n                let contact = Contact(context: taskContext)\n                contact.name = subC.name\n            })\n            book.addToContacts(contact)\n        }\n\n        // Save all insertions and deletions from the context to the store.\n        if taskContext.hasChanges {\n            do {\n                try taskContext.save()\n            } catch {\n                print("Error: \\(error)\\nCould not save Core Data context.")\n                return\n            }\n            // Reset the taskContext to free the cache and lower the memory footprint.\n            taskContext.reset()\n        }\n\n        success = true\n    }\n    return success\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

问题是,这非常慢,因为在每个批次中,我都会获取工作簿(在每次迭代中都会变得更大),以便能够在通讯录中插入新批次的联系人。有没有一种有效的方法来避免在每个批次中获取工作簿?还有什么建议可以让它更快吗?增加批量大小?创建后台队列?

\n\n

更新:

\n\n

我尝试创建一个 ContactBook 一次insertWordbookIfNeeded 并将其传递给importOneBatch每次迭代,但我得到:

\n\n
Thread 1: Exception: "Illegal attempt to establish a relationship \n\'contactBook\' between objects in different contexts\n
Run Code Online (Sandbox Code Playgroud)\n