首页 > 解决方案 > 批量导入包含关系的Core Data中的大集合

问题描述

我正在尝试导入大约 80k 个对象的大型数据集。我正在尝试遵循Apple 示例

我有两个问题:

  1. 在代码示例中有一条注释:
 // taskContext.performAndWait runs on the URLSession's delegate queue
 // so it won’t block the main thread.

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

  1. 在示例中,它只是导入实体数组。但在我的情况下,我只需要导入一个与许多对象相关的具有 70k 个对象的实体。

    在此处输入图像描述

所以我想要实现的是:

我试过的:

private func insertContactbookIfNeeded() {
    let fetch: NSFetchRequest<Contactbook> = ContactBook.fetchRequest()
    let contactBookCount = (try? context.count(for: fetch)) ?? 0

    if contactBookCount > 0 {
        return
    }

    let contacts = Bundle.main.decode([ContactJSON].self, from: "contacts.json")

    // Process records in batches to avoid a high memory footprint.
    let batchSize = 256
    let count = contacts.count

    // Determine the total number of batches.
    var numBatches = count / batchSize
    numBatches += count % batchSize > 0 ? 1 : 0

    for batchNumber in 0 ..< numBatches {

        // Determine the range for this batch.
        let batchStart = batchNumber * batchSize
        let batchEnd = batchStart + min(batchSize, count - batchNumber * batchSize)
        let range = batchStart..<batchEnd

        // Create a batch for this range from the decoded JSON.
        let contactsBatch = Array(contacts[range])

        // Stop the entire import if any batch is unsuccessful.
        if !importOneBatch(contactsBatch) {
            assertionFailure("Could not import batch number \(batchNumber) range \(range)")
            return
        }
    }
}

private func importOneBatch(_ contactsBatch: [ContactJSON]) -> Bool {

    var success = false

    // Create a private queue context.
    let taskContext = container.newBackgroundContext()
    taskContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy

    // NOT TRUE IN MY CASE: (Any suggestion ??)
    // taskContext.performAndWait runs on the URLSession's delegate queue
    // so it won’t block the main thread. 

    print("isMainThread: \(Thread.isMainThread)") // prints true

    taskContext.performAndWait {
        let fetchRequest: NSFetchRequest<ContactBook> = ContactBook.fetchRequest()
        fetchRequest.returnsObjectsAsFaults = true
        fetchRequest.includesSubentities = false

        let contactBookCount = (try? taskContext.count(for: fetchRequest)) ?? 0

        var contactBook: ContactBook?

        if contactBookCount > 0 {
            do {
                contactBook = try taskContext.fetch(fetchRequest).first
            } catch let error as NSError {
                assertionFailure("can't fetch the contactBook \(error)")
            }
        } else {
            contactBook = ContactBook(context: taskContext)
        }

        guard let book = contactBook else {
            assertionFailure("Could not fetch the contactBook")
            return
        }

        // Create a new record for each contact in the batch.
        for contactJSON in contactsBatch {

            // Create a Contact managed object on the private queue context.
            let contact = Contact(context: taskContext)
            // Populate the Contact's properties using the raw data.
            contact.name = contactJSON.name
            contact.subContacts = NSSet(array: contactJSON.subContacts { subC -> Contact in
                let contact = Contact(context: taskContext)
                contact.name = subC.name
            })
            book.addToContacts(contact)
        }

        // Save all insertions and deletions from the context to the store.
        if taskContext.hasChanges {
            do {
                try taskContext.save()
            } catch {
                print("Error: \(error)\nCould not save Core Data context.")
                return
            }
            // Reset the taskContext to free the cache and lower the memory footprint.
            taskContext.reset()
        }

        success = true
    }
    return success
}

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

更新:

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

Thread 1: Exception: "Illegal attempt to establish a relationship 
'contactBook' between objects in different contexts

标签: iosjsonswiftcore-dataimport

解决方案


推荐阅读