使用新的 PageCurl 效果通过 MonoTouch 和 iOS 5.0 读取 PDF 时出现奇怪的崩溃
当使用新的 PageCurl 效果通过 MonoTouch 和 iOS 5.0 读取 PDF 时,我遇到了奇怪的崩溃。 我为 MonoDevelop 2.8 制作了一个简单的测试用例项目,并上传到 GitHub:
https://github.com/Emasoft /IpaziaPDFReader
似乎有什么东西过早地获得 GCd 并杀死了应用程序,但我找不到是什么。我尝试过很多方法来处理一切,但都是徒劳。我已经将项目 tarball 提交给 Xamarin 团队,但他们无法解决问题。
iOS NavigationController 内存管理是否有问题?或者我错过了什么?
任何帮助表示赞赏,谢谢!
更新:我尝试在处理所有类中的对象之前删除所有子视图和子层,但它仍然崩溃。我发现避免崩溃的唯一方法是永远不要处理 PDF 页面,在释放它们之前将它们添加到列表中,但这不是一个可行的解决方案,因为这样,对于包含许多页面的 PDF 来说,内存会快速消耗,并且当无法为下一页分配内存时,应用程序无论如何都会崩溃。 避免崩溃的另一种方法是在翻页之前处理 PDF 页面,在创建新的页面控制器之前强制页面控制器上的 dispose 方法,但这样当前页面将变成空白,并且转换会卷曲一个无用的空页。似乎没有解决方案有效。
我已经用我尝试过的 3 种不同的解决方案更新了 GitHub 上的项目(查看 PageDataSource 类),您可以一次取消注释它们之一以查看问题。
//SOLUTION 1
void ForcingPageControllerDispose (BookPageController oldPageController)
{
// --- IF YOU UNCOMMENT THIS, THE CRASHES GO AWAY, BUT THE PAGE IN THE TRANSITION IS BLANK, SO IS NOT VIABLE
currentPageController.View.RemoveFromSuperview ();
currentPageController.Dispose ();
}
//SOLUTION 2
void DisposeThePageControllerWhenDidFinishAnimating (BookPageController oldPageController, UIPageViewController pageViewController)
{
// --- IF YOU UNCOMMENT THIS, THE CRASHES STILL HAPPEN
pageViewController.DidFinishAnimating += delegate(object sender, UIPageViewFinishedAnimationEventArgs e) {
if (currentPageController != null) {
currentPageController.View.RemoveFromSuperview ();
currentPageController.Dispose ();
Console.WriteLine ("currentPageController disposed for page: " + currentPageController.PageIndex);
}
};
}
//SOLUTION 3
void BackupUnusedPagesToAvoidBeingGCd (BookPageController oldPageController)
{
// --- IF YOU UNCOMMENT THIS, THE CRASHES GO AWAY, BUT THE PAGES ARE NOT GARBAGE COLLECTED AND AFTER MANY PAGES IPHONE IS OUT OF MEMORY AND IT CRASHES THE APP
if (parentController.book_page_controllers_reference_list.Contains (currentPageController) == false)
parentController.book_page_controllers_reference_list.Add (currentPageController);
}
I get a strange crash when using the new PageCurl effect reading a PDF with MonoTouch and iOS 5.0.
I've made a simple test case project for MonoDevelop 2.8 and uploaded on GitHub here:
https://github.com/Emasoft/IpaziaPDFReader
It seems that something is getting GCd too early and killing the application, but I can't find what. I've tried to dispose everything in many ways, but in vain. I've already submitted the project tarball to the Xamarin team, but they weren't able to solve the problem.
Is there something broken in the iOS NavigationController memory management? Or am I missing something?
Any help is appreciated, thanks!
UPDATE: I've tried to remove all subviews and sublayers before disposing the objects in all classes, but it still crashing. The only way I found to avoid the crash is to NEVER dispose of the PDF pages, adding them to a List before releasing them, but this is not a viable solution, because in that way memory is consumed rapidly for PDF with many pages and the app crashes anyway when unable to allocate memory for the next page.
Another way to avoid the crashes is to dispose of the PDF pages BEFORE turning the pages, forcing the dispose method on the page controller before creating a new page controller, but in this way the current page will become blank and the transition curls an useless empty page. No solution seems to work.
I've updated project on GitHub with the 3 different solutions I've tried (look in the PageDataSource class), you can uncomment them one at time to see the problems.
//SOLUTION 1
void ForcingPageControllerDispose (BookPageController oldPageController)
{
// --- IF YOU UNCOMMENT THIS, THE CRASHES GO AWAY, BUT THE PAGE IN THE TRANSITION IS BLANK, SO IS NOT VIABLE
currentPageController.View.RemoveFromSuperview ();
currentPageController.Dispose ();
}
//SOLUTION 2
void DisposeThePageControllerWhenDidFinishAnimating (BookPageController oldPageController, UIPageViewController pageViewController)
{
// --- IF YOU UNCOMMENT THIS, THE CRASHES STILL HAPPEN
pageViewController.DidFinishAnimating += delegate(object sender, UIPageViewFinishedAnimationEventArgs e) {
if (currentPageController != null) {
currentPageController.View.RemoveFromSuperview ();
currentPageController.Dispose ();
Console.WriteLine ("currentPageController disposed for page: " + currentPageController.PageIndex);
}
};
}
//SOLUTION 3
void BackupUnusedPagesToAvoidBeingGCd (BookPageController oldPageController)
{
// --- IF YOU UNCOMMENT THIS, THE CRASHES GO AWAY, BUT THE PAGES ARE NOT GARBAGE COLLECTED AND AFTER MANY PAGES IPHONE IS OUT OF MEMORY AND IT CRASHES THE APP
if (parentController.book_page_controllers_reference_list.Contains (currentPageController) == false)
parentController.book_page_controllers_reference_list.Add (currentPageController);
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我很确定分配给您案件的人员会提出解决方案。测试用例越大,花费的时间就越多。
快速查看 AppDelegate.cs 中的以下内容是错误的:
因为一旦
FinishedLaunching
返回,本地viewController
实例将不会对其有任何引用,并且 GC 将能够收集它。然而,它需要(在本机端)保持View
完全有效。这可能会导致崩溃(也可能存在其他情况,这是我今天早上检查的第一个也是唯一一个文件)。解决方案是将
viewController
提升为字段。即使该方法返回,这也会使其处于活动状态
,从而使其无法被收集。更新
我快速浏览了 github 上的代码。
您正在添加(子)视图,但从未删除它们(当 GC 处理它们时,它不会将它们从超级视图中删除);
您正在丢失对视图的引用,例如在
PageDataSource.cs
在第一个页面之后,已经有一个存储在
newPageController
中的引用,该引用将是覆盖并使对象可收集 bug GC。由于(子)视图永远不会被删除,因此仍然可能对它们进行本机引用,从而导致崩溃。为了调试,您可以添加自己的终结器,例如
并在其中放置断点。如果它们被击中,而您认为它仍在使用,那么您可能发现了问题。
I'm pretty sure the person assigned to your case will come up with the solution. The bigger the test case the more time it can take.
From a quick view the following, in your AppDelegate.cs, is wrong:
since the local
viewController
instance won't have any reference to it onceFinishedLaunching
returns and the GC will be able to collect it. However it's needed (on the native side) for keep theView
fully valid. This can lead to crashes (there could be other cases too, that's the first and only file I checked this morning).The solution is to promote the
viewController
to a field. That will make italive
even when the method returns, making it unavailable to collection.UPDATE
I had a quick look at your code on github.
You are adding (sub)view but you never remove them (when the GC will Dispose them it won't remove them from the super view);
You are losing references to views, e.g. in
PageDataSource.cs
After the first page there will already be a reference stored in
newPageController
which will be overwritten and make the object collectable bug the GC. Since (sub)views are never removed there could still be native reference to them leading to crashes.For debugging you can add your own finalizers, e.g.
and put breakpoints in them. If they get hit, while you think it's still in use, then you likely found a problem.
由于您依赖 iOS 5 新功能,因此您还应该采用 iOS 5 中新的 View Controller Containment API,它可以解决视图控制器的一些问题。
我建议您查看 WWDC 视频和幻灯片,了解第 102 节“实现 UIViewController 遏制”。
Since you are taking a dependency on iOS 5 new features, you should also adopt the new View Controller Containment APIs in iOS 5 that solve a few problems with view controllers.
I suggest you check the WWDC Video and slides for for Session 102 "Implement UIViewController Containment".