我正在向我現有的專案添加等待和并發方法,并遇到了一些我無法除錯的奇怪行為,因為它們不會每次都發生,有時只是隨機發生,在路上的某個地方(在 buildDataStructureNew 內部)
func buildDataStructureNew() async -> String {
var logComment:String = ""
let context = await (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
// Step 1 get JSON data
let jsonData = try? await getRemoteFoo()
guard jsonData != nil else {...}
let feedback2 = await step2DeleteAllEntities(context: context) // Step 2
{...}
let feedback3 = await step3saveJSONtoFoo(context: context, remoteData: remoteData) // Step3
{...}
let sourceData = await step41getAllSavedFoo(context: context) // Step 4.1
{...}
let feedback42 = await step42deleteAllSingleFoos(context: context) //Step 4.2
{...}
let feedback43 = await step43splitRemoteDataIntoSingleDataFoos(context: context, sourceData: sourceData) // Step 4.3
{...}
let feedback5 = await step5createDataPacks() // Step 5
return logComment
}
如您所見,我在相同的背景關系中執行了每個步驟,希望它能夠正常作業,但我開始從步驟 4.3 收到致命錯誤,我無法解釋自己..
CoreData: error: Serious application error. Exception was caught during Core Data change processing.
This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification.
-[__NSCFSet addObject:]: attempt to insert nil with userInfo (null)
所以我嘗試為這一步使用不同的背景關系
let context = await (UIApplication.shared.delegate as! AppDelegate).persistentContainer.newBackgroundContext() // just to test it
以第 3 步為例:
func step3saveJSONtoFoo(context: NSManagedObjectContext, remoteData:[remoteData]) async -> String {
var feedback:String = ""
var count:Int = 0
for i in 0...remoteFoo.count-1 {
let newFoo = remoteFoos(context: context)
newFoo.a = Int16(remoteFoo[i].a)
newFoo.b = remoteFoo[i].b
newFoo.c = remoteFoo[i].c
newFoo.Column1 = remoteFoo[i].Column1
newFoo.Column2 = remoteFoo[i].Column2
newFoo.Column3 = remoteFoo[i].Column3
newFoo.Column4 = remoteFoo[i].Column4
newFoo.Column15 = remoteFoo[i].Column4
count = i
do {
try context.save()
// print("DEBUG - - ? save JSON to RemoteFoo successfull")
} catch {
feedback = "\n-- !!! -- saving JSON Data to CoreData.Foos failed(Step 3), error:\(error)"
Logging.insertError(message: "error saving JSON to CoreData.Foos", location: "buildDataStructureNew")
}
}
// print("DEBUG - - ? Step3 \(count 1) records saved to RemoteFoo")
feedback = feedback "\n-- ? \(count 1) records saved to RemoteFoo"
return feedback
}
這解決了這個問題,但是我從第 5 步得到了同樣的錯誤,所以我也將背景背景關系添加到這一步,乍一看這又解決了,我以為就是這樣,但幾分鐘后方法崩潰了再次在我身上,但現在在第 3 步,再次出現相同的錯誤訊息。
據我了解,這與我使用的背景關系有關,但我并沒有真正理解這里的真正問題。
我以前對這種方法沒有任何問題,當我將該方法重寫為異步時,這開始發生在我身上。
現在該方法作業正常,沒有任何問題,或者至少我目前無法重現它,但它可能很快就會回來..我想我在這里缺少一些理解,我希望你們能幫助我
uj5u.com熱心網友回復:
您看到的問題是因為 Core Data 對并發有自己的想法,這些想法不直接映射到您可能使用的任何其他并發技術。有時但并非總是出現不一致的錯誤是并發問題的典型標志。
對于并發 Core Data 使用,您必須使用背景關系perform { ... }或所有Core Data 訪問。Async/await 不是替代品,也不是您在 iOS 應用程式的其他地方用于并發的任何其他東西。這包括以任何方式觸及核心資料的所有內容——獲取、保存、合并、訪問托管物件上的屬性等。你沒有這樣做,這就是你遇到這些問題的原因。performAndWait { ... }DispatchQueue
唯一的例外是如果您的代碼在主佇列上運行并且您有一個主佇列背景關系。
在你處理這個問題時,你應該通過使用-com.apple.CoreData.ConcurrencyDebug 1作為應用程式的引數來打開 Core Data 并發除錯。這在包括這篇文章在內的各種博客文章中都有描述。
uj5u.com熱心網友回復:
在湯姆的解釋的幫助下,我能夠解決這個問題以確保一切都以正確的順序運行,在 .perform 或 performAndWait 呼叫中執行這些核心資料呼叫很重要
由于第 1 步是唯一真正的異步操作(網路 API 訪問),因此這是唯一標記為“異步/等待”的步驟,希望所有其他步驟現在都通過 CoreData 自己的佇列正確排隊。
這是我的最終作業代碼
func buildDataStructureNew() async -> String {
var logComment:String = ""
let context = await (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
// Step 1 get JSON data
let jsonData = try? await getRemoteFoo()
guard jsonData != nil else {...}
let feedback2 = step2DeleteAllEntities(context: context) // Step 2
{...}
let feedback3 = step3saveJSONtoFoo(context: context, remoteData: remoteData) // Step3
{...}
let sourceData = step41getAllSavedFoo(context: context) // Step 4.1
{...}
let feedback42 = step42deleteAllSingleFoos(context: context) //Step 4.2
{...}
let feedback43 = step43splitRemoteDataIntoSingleDataFoos(context: context, sourceData: sourceData) // Step 4.3
{...}
let feedback5 = step5createDataPacks() // Step 5
return logComment
}
并再次以第 3 步為例
func step3saveJSONtoFoo(context: NSManagedObjectContext, remoteData:[remoteData]) -> String {
var feedback:String = ""
var count:Int = 0
for i in 0...remoteFoo.count-1 {
let newFoo = remoteFoos(context: context)
newFoo.a = Int16(remoteFoo[i].a)
newFoo.b = remoteFoo[i].b
{...}
count = i
context.performAndWait {
do { try context.save() }
catch let error {
feedback = "\n-- !!! -- saving JSON Data to CoreData.Foos failed(Step 3), error:\(error)"
Logging.insertError(message: "error saving JSON to CoreData.Foos", location: "buildDataStructureNew")
}
}
}
feedback = feedback "\n-- ? \(count 1) records saved to RemoteFoo"
return feedback
}
最后一點:Xcode 最初告訴我“performAndWait”僅適用于 iOS 15 及更高版本,這是真的,因為蘋果發布了此功能的新版本,標記為“rethrow”,但也有舊版本功能可用,并且此版本的“執行”可從 iOS 5 及更高版本獲得;)
訣竅是不要忘記在 do 陳述句之后使用自己的 catch 塊;)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/515423.html
上一篇:如何一次更改多個正方形顏色?
