在 Xamarin.iOS 应用程序中,出于各种原因我们不能使用 async
/await
。因此,我们正在使用 Task.ContinueWith
。问题是我们在延续中依赖的对象在调用延续之前就被释放了。
using (UIImage image = // Somehow get the image)
{
DoSomethingWith(image);
}
void DoSomethingWithImage(UIImage image)
{
UploadImageAsync(image).ContinueWith(t =>
{
// Image has been disposed by this point.
DisplayResult(t.Result, image);
});
}
我很想知道如何处理上述案例。我考虑过的选项:
using
block ,并让 image
超出范围。这将是一个坏主意,因为 UIImage
可能会非常大。using
,并在continuation中调用Dispose()
。这感觉很容易出错,因为无论哪里存在这样的代码,您都会将对象放置在远离其创建位置的地方。DoSomethingWithImage()
的调用包装在 Task.Run(() => DoSomethingWithImage(image)).ContinueWith(t => image.Dispose())
。所以你有一个延续中的延续。对于这种情况,这感觉过于复杂。最佳答案
首先,这是一个很好的问题。根据上述选项,我认为您必须更改 angel。
在您当前的实现中,UIImage
的范围与 DoSomethingWith
方法密切相关。换句话说,检索到的图像仅在该方法内部使用。我可以看到两种不同的方式,这将如何演变:
DoSomethingElseWith
方法DoSomethingWith
并“内联” UploadImage
和 DisplayResult
无论哪种方式,您最终都会在 using
block 中包含多个方法。这将表明 UIImage 的清理不是方法的责任。在正常情况下,您会通过 await
最后一个并让 using block 完成其工作来完成此操作,对吗?如果您不能等待它,那么继续的最明显选择是使用 ContinueWith
。 (其他: TaskFactory.ContinueWhenAll
, TaskFactory.ContinueWhenAny
, Task.WhenAll
, Task.WhenAny
, for further info ) .
UIImage image = RetrieveImage();
var uploadJob = UploadImage(image);
var displayJob = uploadJob.ContinueWith((t => DisplayResult(t.Result, image)).Unwrap();
var disposeJob = displayJob.ContinueWith(_ => image.Dispose());
或者,您可以使用一些短信号机制(同步原语)来指示最后一个异步操作何时完成,例如 AutoResetEvent
、CountdownEvent
、TaskCompliitionSource
.
您的第三个选择可能是使用 TPL Dataflow 。有了它,您可以将小块组合在一起,并使用 Completion 启动处置作为您的最后一个操作。
https://stackoverflow.com/questions/62494305/