UI 在 URLSession Background-DownloadTask 期间重新输入 Detail-VC 后冻结

UI freezes after re-entering Detail-VC during URLSession Background-DownloadTask

使用 Swift-4.0.3、iOS-11.2、Xcode-9.2、iPhone-6S(或 Simulator-10.0)

大约一个星期以来,我试图找出以下问题的原因:每当您重新输入涉及 URLSession-Background-DownloadTask 的 ViewController 时,UI 就会冻结。通过重新输入,我的意思是:从 VC 到 Detail-VC 并返回到 VC...然后再次从 VC 到详情-VC.

请在 github 上找到我的示例项目:https://github.com/korners/Test00008

示例项目使用MZDownloadManager from mzeeshanid。我也尝试了其他框架 - 同样的问题。 MZDownloadManager 是我发现的一个非常好的实现。

回到问题:现在-关于详细信息的第一个条目-VC:一切运行顺利,因为它应该(没有问题)。即使是关闭的应用程序也会顺利启动已经 运行 的后台下载任务(没有问题 - 即进度条和 UI-标签更新得很好)。

但是如果用户按下 Detail-VC(NavigationController 的顶部栏)上的后退按钮 - 从这一刻起,Detail-VC 只能在冻结的默认状态下看到!(不再有 UI 更新或进度条移动)。

我非常感谢对此的任何帮助!

P.S。顺便说一下,造成问题的不是 Segue 本身。我还尝试从情节提要中实例化 VC - 同样的事情:RE-ENTERING 冻结了 UI :/

经过更多的调试,我发现在 Detail-VC 的 re-enter 处,URLSession 的委托是 nil

事实证明 -(如果设置为 background-task)- 之前下载的 URLSession 仍然存在,不会创建新的 URLSession 对象,而是 returns 现有的 附加了旧的代表对象 !!!

项目的设置方式:按back-button(或离开当前Detail-VC)将丢弃ViewController(包括其downloadManager-property 和他的代表)。因此,如果 re-entering Detail-VC,那么从之前的 运行 启动的 URLSession 将尝试将其 delegate-methods 发送到不再存在的 URLSession-delegate! ! (即离开 Detail-VC 时被杀死的那个)。

有趣的是,如果App关闭后re-started,那么Background-URL-session的delegate可以重新设置。很明显,只要应用程序处于 运行ning 状态,就会保留委托。

大声思考,...我想解决这个问题的一种方法是,例如让 View-object 保持活动状态(并将其交给 background-URLSession 作为其代表),然后每当有需要时(使用 addsubView(View) 或类似的)呈现此 View-object。但总的来说,在整个应用程序中使用唯一的 View-object 是个坏主意。

我想,解决此问题的最佳方法是首先创建一个 singleton-URLSession-object(它始终存在并且不受任何 Navigation-controller segue'ing 的影响)。因此它的代表在整个应用程序中都存在。

我现在的 take-home 消息:请注意您指定给 BACKGROUND-URLSESSION 的人作为其代表以及您的 APP-LIFECYCLE 使用它做什么(它不会轻易放开它 - 即使 URLSession 对象(及其代表一起)已经死了。;)它只会在你关闭应用程序和 re-start 时放开它)

希望,我明白了吗?

非常感谢对此主题的任何建议或评论!

是的,我认为你是对的。

在我的例子中,我有一个基于 VC 的列表(我们称之为父 VC)和详细信息 VC。 Detail VC 将创建一个后台会话并将自己设置为它的委托。我还注意到,如果我退出并 re-enter detail VC 就会出错。我得出了同样的结论,会话正在更新第一个细节 VC,我以为已经消失了,当我从它导航回来时被杀死了。

你看,第一个细节 VC 将会话创建为 属性,因此它有一个指向会话的指针。它将自己设置为会话的委托,因此现在会话有一个指向它的指针。这称为循环,是内存泄漏的原因。这就是为什么第一个细节 VC 没有被杀死——会话仍然指向它。通常这可以通过在一个方向上使用弱引用来解决,但正如您已经发现的那样,这里还有另一个问题:您不能四处走动并创建多个具有相同标识符的后台会话。您需要创建一个并将其作为单例保留。