首页 文章

iOS CATiledLayer崩溃

提问于
浏览
3

我有一个适用于iPad的pdf阅读器应用程序,我使用滚动视图来显示每个页面 . 我将页面保持在视图中,并在页面的任一侧查看一页 . 我有纵向和横向视图的独立视图 . 纵向视图显示单个页面,横向查看器显示2个页面 .

当iPad改变方向时,我会卸载旧方向的视图并加载新方向的视图 . 所以说它是在纵向视图中然后更改为横向应用程序卸载纵向视图并加载横向视图 . 这一切都很有效,除非pdf很大 .

pdf是使用tileslayers绘制的 . 当方向改变为大pdf时,应用程序正在进行清理 . 如果在绘制完所有图块之前更改了方向,则应用程序仅会崩溃 . 我的猜测是它崩溃了,因为它试图将瓷砖绘制到视图而不是已卸载 . 那么当我卸载视图时有没有办法停止绘制图块?

2 回答

  • 4

    您需要将CALayer的委托设置为nil,然后将其从superview中删除 . 这会停止渲染,之后您可以安全地释放 .

    - (void)stopTiledRenderingAndRemoveFromSuperlayer; {
        ((CATiledLayer *)[self layer]).delegate = nil;    
        [self removeFromSuperview];
        [self.layer removeFromSuperlayer];
    }
    

    另外,请确保从主线程中调用此方法,否则可能会出现可怕的错误 .

  • 3

    我没有看过反汇编看,但我们使用的是一个略有不同的解决方案 . 将 CATiledLayer.content 属性设置为 nil 块并强制完成所有排队的渲染块 . 这可以安全地启动到后台线程,然后释放 UIView 可以被踢回主线程让视图和图层dealloc .

    这是 UIViewController dealloc 实现的一个示例,它将使您的 CATiledLayer 拥有视图保持足够长的时间以安全地停止渲染,而不会阻塞主线程 .

    - (void)dealloc
    {
        // This works around a bug where the CATiledLayer background drawing 
        // delegate may still have dispatched blocks awaiting rendering after
        // the view hierarchy is dead, causing a message to a zombie object.
        // We'll hold on to tiledView, flush the dispatch queue, 
        // then let go of fastViewer.
        MyTiledView *tiledView = self.tiledView;
        if(tiledView) {
            dispatch_background(^{
                // This blocks while CATiledLayer flushes out its queued render blocks.
                tiledView.layer.contents = nil;
    
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                    // Make sure tiledView survives until now.
                    tiledView.layer.delegate = nil;
                });
            });
        }
    }
    

    This is a guess ,但Apple的一些框架/类(StoreKit,CATiledLayer,UIGestureRecognizer)声称有 @property (weak) id delegate 实现,但显然没有正确处理 weak 委托 . 看着一些反汇编,他们正在进行明确的竞赛 if != nil 检查,然后直接触及弱房产 . 正确的方法是声明一个 __strong Type *delegate = self.delegate ,它会成功并给你一个强大的参考保证生存,或者是 nil ,但它肯定不会给你一个zombie对象的引用(我的猜测是框架代码没有已升级为ARC) .

    在引擎盖下, CATiledLayer 创建一个调度队列来进行背景渲染,并且看起来要么以不安全的方式触摸委托属性,要么获得本地引用但不使其成为强引用 . 无论哪种方式,如果委托被取消分配,调度的渲染块将很乐意向僵尸对象发送消息 . 只是清除代表是不够的 - 它将减少崩溃的数量,但不能安全地消除它们 .

    设置 content = nil 会执行dispatch_wait并阻塞,直到所有现有的排队渲染块都完成为止 . 我们回到主线程以确保 dealloc 是安全的 .

    如果有人有改进建议,请告诉我 .

相关问题