我有一个ViewController,可从其中下载pdf文档。
下载时,我正在显示一个UIAlertController,其中包含一个UIProgressView,我将根据下载进度进行更新。第一次一切都很好。
现在,下载后,我按导航栏中的“后退”按钮转到上一个ViewController。然后,当我再次前进到下载控制器并尝试再次下载时,进度没有更新,并且UIAlertController也没有关闭。
问题只有当我回到上一个控制器时。如果我留在同一个控制器中,然后再次尝试下载,则可以正常工作。
我已经坚持了一段时间了。任何帮助表示赞赏。
public partial class WAReportController : UITableViewController
{
const string Identifier = "com.gch.DownloadDocument.BackgroundSession";
public NSUrlSessionDownloadTask downloadTask;
public NSUrlSession session;
public void DownloadReport()
{
if (session == null)
session = InitBackgroundSession();
using (var url = NSUrl.FromString(RestApiPaths.REPORT_DOWNLOAD_PATH))
using (var request = new NSMutableUrlRequest(url)) {
request.Headers = CommonUtils.GetHeaders();
downloadTask = session.CreateDownloadTask(request);
downloadTask.Resume();
ShowAlert();
}
}
public NSUrlSession InitBackgroundSession()
{
Console.WriteLine("InitBackgroundSession");
using (var configuration = NSUrlSessionConfiguration.CreateBackgroundSessionConfiguration(Identifier)) {
return NSUrlSession.FromConfiguration(configuration, (INSUrlSessionDelegate)new ReportDownloadDelegate(this), new NSOperationQueue());
}
}
public class ReportDownloadDelegate : NSUrlSessionDownloadDelegate
{
private WAReportController _vc;
public ReportDownloadDelegate(WAReportController vc)
{
_vc = vc;
}
public override void DidWriteData(NSUrlSession session, NSUrlSessionDownloadTask downloadTask, long bytesWritten, long totalBytesWritten, long totalBytesExpectedToWrite)
{
float progress = totalBytesWritten / (float)totalBytesExpectedToWrite;
Console.WriteLine(string.Format("progress: {0}", progress));
DispatchQueue.MainQueue.DispatchAsync(() => {
_vc.UpdateDownloadProgress(progress); // updates successfully only the first time
});
}
public override void DidFinishDownloading(NSUrlSession session, NSUrlSessionDownloadTask downloadTask, NSUrl location)
{
_vc.DismissDownloadProgressAlert();
}
}
UIAlertController downloadProgressAlert;
UIProgressView downloadProgress;
void ShowAlert()
{
downloadProgressAlert = UIAlertController.Create("Downloading", "\n\n", UIAlertControllerStyle.Alert);
downloadProgressAlert.AddAction(UIAlertAction.Create("Cancel", UIAlertActionStyle.Cancel, (action) => {
downloadTask.Cancel();
}));
PresentViewController(downloadProgressAlert, true, () => {
nfloat margin = 8.0f;
var rect = new CGRect(margin, 72.0f, downloadProgressAlert.View.Frame.Width - margin * 2.0f, 2.0f);
downloadProgress = new UIProgressView(rect) {
Progress = 0.0f,
TintColor = UIColor.Blue
};
downloadProgressAlert.View.AddSubview(downloadProgress);
});
}
public void UpdateDownloadProgress(float progress)
{
if (downloadProgress != null) {
downloadProgress.Progress = 50;
}
}
public void DismissDownloadProgressAlert()
{
if (downloadProgressAlert != null) {
InvokeOnMainThread(() => {
downloadProgressAlert.DismissViewController(false, null);
});
}
}
}
}
最佳答案
问题是您在NSURLSession
对象中拥有对NSURLSessionDelegate
实例的强引用,并且从未使会话无效。根据Apple's Documentation
在您的应用退出或显式使会话无效之前,会话对象始终对此委托保持强烈引用。如果您不使会话无效,则您的应用程序会泄漏内存,直到退出。
您应该在某个时候使会话无效。同样,您似乎在这里创建了强大的参考周期:WAReportController
对NSUrlSession
有很强的引用; NSURLSession
对ReportDownloadDelegate
有很强的引用; ReportDownloadDelegate
对WAReportController
;有很强的引用。您在这里看到问题了吗?
您应尽可能使用弱引用。尝试这样的事情:
public class ReportDownloadDelegate : NSUrlSessionDownloadDelegate
{
private WeakReference<WAReportController> _vc;
public ReportDownloadDelegate(WAReportController vc)
{
_vc = vc;
}
public override void DidWriteData(NSUrlSession session, NSUrlSessionDownloadTask downloadTask, long bytesWritten, long totalBytesWritten, long totalBytesExpectedToWrite)
{
if (_vc != null) {
float progress = totalBytesWritten / (float)totalBytesExpectedToWrite;
Console.WriteLine(string.Format("progress: {0}", progress));
DispatchQueue.MainQueue.DispatchAsync(() => {
_vc.UpdateDownloadProgress(progress); // updates successfully only the first time
});
}
}
public override void DidFinishDownloading(NSUrlSession session, NSUrlSessionDownloadTask downloadTask, NSUrl location)
{
if (_vc != null) {
_vc.DismissDownloadProgressAlert();
}
}
}
并在您的视图控制器类中:
public void DismissDownloadProgressAlert()
{
if (downloadProgressAlert != null) {
InvokeOnMainThread(() => {
downloadProgressAlert.DismissViewController(false, null);
});
}
session.InvalidateAndCancel() // I didn't type in an IDE so not sure if this is the exact method signature.
session = null;
}
这样,下载任务完成后,您将关闭进度警报并使会话无效。
NSURLSession
将依次释放WAReportController
实例。现在,您不必在DismissDownloadProgressAlert()
方法中使会话无效。但是使用完会话后,您应该使会话无效。可能是您解雇了视图控制器时或您认为合适时。我只是以这里为例。现在,所有这些都基于Apple的文档以及我对iOS上的内存管理的了解。我从未在Xamarin上工作过,并且与本地iOS开发相比如何使用弱引用可能是错误的。希望这可以帮助!
关于ios - NSUrlSession仅在第一次更新UI控件,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/53325130/