ios - 批量导入包含关系的Core Data中的大集合
问题描述
我正在尝试导入大约 80k 个对象的大型数据集。我正在尝试遵循Apple 示例
我有两个问题:
- 在代码示例中有一条注释:
// taskContext.performAndWait runs on the URLSession's delegate queue
// so it won’t block the main thread.
但就我而言,我没有使用 URLSession 来获取 JSON。该文件与应用程序捆绑在一起。在这种情况下如何确保导入不会阻塞主线程。我应该创建一个自定义队列吗?有什么例子吗?
所以我想要实现的是:
- 如果有一个
ContactBook
不要导入任何东西,因为我们已经导入了 JSON。 - 如果没有
ContactBook
创建一个并将所有 70kContact
对象导入contacts
到ContactBook
. 这应该像示例中那样分批发生,并且不应阻塞 UI。
我试过的:
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
解决方案
推荐阅读
- javascript - 启动另一个时不要隐藏 SweetAlert2
- reactjs - 在从 CDN 加载的 React 中使用外部依赖项
- android - 更新到 4.0 后无法在 Android Studio 中创建 Activity 或 Fragment
- cocoa - 为什么我的按钮在 High Sierra 中消失了
- qemu - 在 mmio 期间检索 QEMU VM 的堆栈跟踪
- python - 如何通过 Android 手机上的应用程序向我的 Python 脚本发送命令?
- google-cloud-platform - Ansible 将自定义角色或权限绑定到 GCP IAM
- c - 带有 OpenSSL 的 C 语言中的 HMAC-SHA256 - 如何获得正确的输出
- pandas - 如何访问默认的熊猫数据框绘图调色板?
- reactjs - 无法使用 fetch() 从 carquery API 获取 API