ios SWIFT任务继续误用:泄漏了它的延续,在for循环之后无法返回结果

zi8p0yeb  于 2023-06-25  发布在  iOS
关注(0)|答案(1)|浏览(138)

我收到以下警告:
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);
        }
    }
}

需要修复以避免-
SWIFT任务继续误用:saveAndClose()泄露了它的续篇!
并在执行for循环的所有项后正确返回result。

lf3rwulv

lf3rwulv1#

考虑错误:
SWIFT任务继续误用:saveAndClose()泄露了它的续篇!
这意味着您有一些从未调用其完成处理程序的执行路径,因此延续被泄露。(这并不是说您调用了完成处理程序太多次,因为这是不同的错误消息。)
我没有看到一个明显的执行路径,其中忽略了调用完成处理程序。到目前为止,您与我们分享的代码中似乎没有问题:但是您也有自定义方法,这些方法有自己的完成处理程序,例如saveDocumentcloseDocument,您没有与我们共享,所以可能其中一个方法有一个执行路径,它无法调用其完成处理程序。因此,我们不能确切地说你的问题在哪里。但是,底线是,您可能没有在某处调用完成处理程序。您可能需要插入一些日志记录语句和/或断点,并查看是否可以诊断在哪里未能调用相关的完成处理程序。
考虑这个问题的可重复的例子。下面的基于completion-handler的例程在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 Package 器:

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

但是如果我用一个负值调用它,我会得到“leaked its continuation”错误(因为我的遗留函数有一个错误,只有在值为正时才调用闭包):

let value = await experiment.squareRoot(of: -2)
print(value)                                    // SWIFT TASK CONTINUATION MISUSE: squareRoot(of:) leaked its continuation!

显然,解决方法是每个执行路径都必须调用其完成处理程序。

相关问题