提问人:Narayana MV 提问时间:6/21/2023 最后编辑:Narayana MV 更新时间:7/6/2023 访问量:661
SWIFT TASK CONTINUATION MISUSE:泄露了其延续,在 FOR 循环后无法返回结果
SWIFT TASK CONTINUATION MISUSE: leaked its continuation and unable to return result after for loop
问:
我收到以下警告:
SWIFT 任务延续误用:saveAndClose() 泄露了其延续!
在for循环中,在执行了几个项目后,在其中一个项目中,在保存和关闭功能中,它被阻止,并显示上述错误,并且没有移动到下一个项目,因此无法返回结果。
这里可能有什么问题,有什么方法可以优化这些片段中的任何一个?
private func processTags(reqItems: [FTShelfItemProtocol], selectedTags: [String]) async throws -> FTShelfTagsResult { let items: [FTDocumentItemProtocol] = reqItems.filter({ ($0.URL.downloadStatus() == .downloaded) }).compactMap({ $0 as? FTDocumentItemProtocol })
var totalTagItems: [FTShelfTagsItem] = [FTShelfTagsItem]()
for case let item in items where item.documentUUID != nil {
guard let docUUID = item.documentUUID else { continue }//, item.URL.downloadStatus() == .downloaded else { continue }
let destinationURL = FTDocumentCache.shared.cachedLocation(for: docUUID)
print(destinationURL.path)
// move to post processing phace
do {
let document = await FTNoteshelfDocument(fileURL: destinationURL)
let isOpen = try await document.openDocument(purpose: FTDocumentOpenPurpose.read)
if isOpen {
let tags = await document.documentTags()
let considerForResult = selectedTags.allSatisfy(tags.contains(_:))
if considerForResult && !tags.isEmpty {
var tagsBook = FTShelfTagsItem(shelfItem: item, type: .book)
tagsBook.tags = tags
totalTagItems.append(tagsBook)
}
}
let tagsPage = await document.fetchSearchTagsPages(shelfItem: item, selectedTags: selectedTags)
totalTagItems.append(contentsOf: tagsPage)
_ = await document.saveAndClose()
} catch {
cacheLog(.error, error, destinationURL.lastPathComponent)
}
}
cacheLog(.success, totalTagItems.count)
let result = FTShelfTagsResult(tagsItems: totalTagItems)
return result
}
func saveAndClose() async -> Bool {
return await withCheckedContinuation({ continuation in
self.saveAndCloseWithCompletionHandler { isSuccess in
continuation.resume(returning: isSuccess)
}
})
}
func saveAndCloseWithCompletionHandler(_ onCompletion :((Bool) -> Void)?)
{
FTCLSLog("Doc: Save and Close");
self.prepareForClosing();
self.saveDocument { (saveSuccess) in
if(saveSuccess) {
self.closeDocument(completionHandler: { (_) in
onCompletion?(saveSuccess);
});
}
else {
onCompletion?(saveSuccess);
}
}
}
func saveDocument(completionHandler : ((Bool) -> Void)?)
{
if(self.openPurpose == .read) {
FTLogError("Doc Saved in Readonly");
completionHandler?(true);
return;
}
if(self.hasAnyUnsavedChanges) {
(self.delegate as? FTNoteshelfDocumentDelegate)?.documentWillStartSaving(self);
}
FTCLSLog("Doc: Save");
#if !NS2_SIRI_APP
self.recognitionCache?.saveRecognitionInfoToDisk(forcibly: true);
if(self.hasAnyUnsavedChanges) {
if let cache = self.recognitionCache, let cachePlist = cache.recognitionCachePlist() {
let mutableDict = NSMutableDictionary.init(dictionary: cachePlist.contentDictionary);
self.recognitionInfoPlist()?.updateContent(mutableDict);
}
if let cache = self.recognitionCache, let cachePlist = cache.visionRecognitionCachePlist() {
let mutableDict = NSMutableDictionary.init(dictionary: cachePlist.contentDictionary);
self.visionRecognitionInfoPlist()?.updateContent(mutableDict);
}
//This was added in version 6.2, when we removed the bounding rect from the Segment level storage.
updateDocumentVersionToLatest()
}
#endif
super.save { (success) in
if(success) {
self.previousFileModeificationDate = self.fileModificationDate;
let pages = self.pages();
for eachPage in pages {
eachPage.isDirty = false;
}
}
completionHandler?(success);
}
}
func closeDocument(completionHandler: ((Bool) -> Void)?)
{
FTCLSLog("Doc: Close");
#if !NS2_SIRI_APP
self.recognitionCache?.saveRecognitionInfoToDisk(forcibly: true)
#endif
super.close { (success) in
self.removeObservers();
completionHandler?(success);
}
}
需要修复以避免 -
SWIFT 任务延续误用:saveAndClose() 泄露了其延续!
并在执行 for 循环的所有项目后正确返回结果。
答:
请考虑以下错误:
SWIFT 任务延续误用:saveAndClose() 泄露了其延续!
这意味着您有一些从未调用其完成处理程序的执行路径,因此延续被泄露了。(这并不是说您调用完成处理程序的次数过多,因为这是不同的错误消息。
我没有看到明显的执行路径,您忽略了调用完成处理程序。到目前为止,您与我们共享的代码中似乎没有问题: 但是您也有自定义方法,这些方法具有自己的完成处理程序,例如 和 您尚未与我们共享,因此可能其中一个方法具有执行路径,其中无法调用其完成处理程序。因此,我们无法确切地说出问题出在您的案例中。但是,归根结底,您可能没有在某处调用完成处理程序。您可能需要插入一些日志记录语句和/或断点,并查看是否可以诊断出相关完成处理程序调用失败的位置。saveDocument
closeDocument
考虑这个问题的这个可重现的例子。以下基于完成处理程序的例程,在 1 秒后返回某个数字的平方根(我只是模拟一些遗留的异步过程):
private func legacySquareRoot(of value: Double, completion: @escaping (Double) -> Void) {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
if value >= 0 {
completion(sqrt(value))
}
}
}
显然,我不想计算负数的平方根。但上述情况是不正确的,因为并非每个执行路径都会调用其完成处理程序。(这是我们更喜欢 Swift 并发而不是完成处理程序模式的原因之一,因为此问题不会在本机 Swift 并发代码中发生。
无论如何,让我们假设我们为这个遗留函数编写了一个包装器:async
func squareRoot(of value: Double) async -> Double {
await withCheckedContinuation { continuation in
legacySquareRoot(of: value) { result in
continuation.resume(returning: result)
}
}
}
如果我用正值调用它,一切都很好:
let value = await experiment.squareRoot(of: 2)
print(value) // 1.4142135623730951
但是,如果我用负值调用它,我会得到“泄露其延续”错误(因为我的旧函数有一个错误,只有在值为正时才调用闭包):
let value = await experiment.squareRoot(of: -2)
print(value) // SWIFT TASK CONTINUATION MISUSE: squareRoot(of:) leaked its continuation!
显然,解决方法是每个执行路径都必须调用其完成处理程序。
评论
super.save
super.close
评论
saveAndClose()
saveAndCloseWithCompletionHandler()