我有一个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


  在您的应用退出或显式使会话无效之前,会话对象始终对此委托保持强烈引用。如果您不使会话无效,则您的应用程序会泄漏内存,直到退出。


您应该在某个时候使会话无效。同样,您似乎在这里创建了强大的参考周期:

WAReportControllerNSUrlSession有很强的引用; NSURLSessionReportDownloadDelegate有很强的引用; ReportDownloadDelegateWAReportController;有很强的引用。您在这里看到问题了吗?

您应尽可能使用弱引用。尝试这样的事情:

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/

10-11 14:55