3

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

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

pdf 是使用 tiledlayers 绘制的。当使用大 pdf 更改方向时,应用程序正在崩溃。仅当在所有图块绘制完成之前更改方向时,应用程序才会崩溃。我的猜测是它正在崩溃,因为它试图将图块绘制到视图中而不是已卸载。那么有没有办法在我卸载视图时停止绘制瓷砖?

4

2 回答 2

5

您需要将 CALayer 的委托设置为 nil,然后将其从超级视图中删除。这会停止渲染,之后您可以安全地解除分配。

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

另外,确保从主线程调用它,否则可怕的错误将等待你。

于 2011-08-01T23:44:30.963 回答
3

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

这是一个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;
            });
        });
    }
}

这是一个猜测,但 Apple 的一些框架/类(StoreKit、CATiledLayer、UIGestureRecognizer)声称有@property (weak) id delegate实现但显然没有正确处理weak委托。看着一些拆卸,他们正在做明确的种族if != nil检查,然后直接接触弱属性。正确的方法是声明 a __strong Type *delegate = self.delegate,它要么成功并给你一个保证生存的强引用,要么是nil,但它肯定不会给你一个僵尸对象的引用(我的猜测是框架代码没有升级到弧)。

在后台,CATiledLayer创建了一个调度队列来进行后台渲染,并且似乎以不安全的方式接触了委托属性,或者它获得了本地引用但没有使其成为强引用。无论哪种方式,如果委托被释放,分派的渲染块将愉快地向僵尸对象发送消息。仅仅清除委托是不够的——它会减少崩溃的数量,但不能安全地消除它们。

设置content = nildispatch_wait 并阻塞,直到所有现有的排队渲染块完成。我们反弹回主线程以确保它dealloc是安全的。

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

于 2014-12-01T20:10:32.190 回答