停止CATiledLayer的绘制

10

如何停止 CATiledLayer 进行绘制(drawLayer:inContext)?它会异步绘制,并且当我尝试释放被 CATiledLayer 使用的 CGPDFDocumentRef 时,应用程序会崩溃(EXC_BAD_ACCESS)。

这是我的观点:

@implementation TiledPDFView

- (id)initWithFrame:(CGRect)frame andScale:(CGFloat)scale{
    if ((self = [super initWithFrame:frame])) {

        CATiledLayer *tiledLayer = (CATiledLayer *)[self layer];
        tiledLayer.levelsOfDetail = 4;
        tiledLayer.levelsOfDetailBias = 4;
        tiledLayer.tileSize = CGSizeMake(512.0, 512.0);
        myScale = scale;
    }
    return self;
}

// Set the layer's class to be CATiledLayer.
+ (Class)layerClass {
    return [CATiledLayer class];
}

- (void)stopDrawing{
    CATiledLayer *tiledLayer = (CATiledLayer *)[self layer];
    [tiledLayer removeFromSuperlayer];
    tiledLayer.delegate = nil;
}
// Set the CGPDFPageRef for the view.
- (void)setPage:(CGPDFPageRef)newPage
{
    CGPDFPageRelease(self->pdfPage);
    self->pdfPage = CGPDFPageRetain(newPage);

    //self->pdfPage = newPage;
}


-(void)drawRect:(CGRect)r
{
}


// Draw the CGPDFPageRef into the layer at the correct scale.
-(void)drawLayer:(CALayer*)layer inContext:(CGContextRef)context
{   

    // First fill the background with white.
    CGContextSetRGBFillColor(context, 1.0,1.0,1.0,1.0);
    CGContextFillRect(context,self.bounds);

    CGContextSaveGState(context);
    // Flip the context so that the PDF page is rendered
    // right side up.
    CGContextTranslateCTM(context, 0.0, self.bounds.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);

    // Scale the context so that the PDF page is rendered 
    // at the correct size for the zoom level.
    CGContextScaleCTM(context, myScale,myScale);    
    CGContextDrawPDFPage(context, pdfPage);
    CGContextRestoreGState(context);

}

// Clean up.
- (void)dealloc {
    CGPDFPageRelease(pdfPage);

    [super dealloc];
}

以下是我尝试在视图控制器中停止和释放PDF的代码:

vTiledPDFView 的实例。

 -(void) stopDwaring {
     [v stopDrawing];
     [v removeFromSuperview];
     [v release];
     [self.view removeFromSuperview];
     self.view = nil;
     CGPDFDocumentRelease(pdf);

 }

请编辑您的问题,包括崩溃日志。 - Peter Hosey
6个回答

9

这篇文章帮助我解决了使用CATiledLayer时遇到的问题。我使用了苹果文档中的TiledPDFview.m作为示例。由于在某些情况下需要重新绘制整个视图和所有瓦片,所以我将CATiledLayer作为属性使用。但是,在退出和释放视图控制器时,程序崩溃并出现 [CATiledLayer retain]: Message sent to deallocated instance 的错误。以下是我的视图控制器dealloc方法:

- (void)dealloc {
    self.tiledLayer.contents=nil;
    self.tiledLayer.delegate=nil;
    [self.tiledLayer removeFromSuperlayer];

    // note: releasing the layer still crashes- 
    // I guess removeFromSuperlayer releases it already, 
    // but couldn't find documentation so far.
    // So that's why it's commented out:
    // [self.tiledLayer release], self.tiledLayer=nil;

    //release the other viewcontroller stuff... 
    [super dealloc];
}

这对我很有帮助,希望能帮到其他人。


在许多答案中,我解决了这个问题。这个答案是最好的,它描述了我所需要的解决方案。 - negersiu
如果该属性被标记为“retain”,那么 [self.tiledLayer release], self.tiledLayer=nil; 崩溃就不足为奇了:这行代码会过度释放对象。现在它只是泄漏了... - DarkDust

3

声明一个TiledPDFView对象pdfView;

在pdfView的父视图类的dealloc中,写入以下代码。

  - (void)dealloc {
        if (nil != self.pdfView.superview) {
            self.pdfView.layer.delegate = nil;
            [self.pdfView removeFromSuperview];
        }
 }

这对我有用,希望能帮到你。


3
在释放CGPDFDocumentRef之前,先将CATiledLayer从其父图层中移除。
[yourTiledLayer removeFromSuperlayer];

不要忘记将其委托对象设置为nil。
yourTiledLayer.delegate = nil;

之后,您可以安全地释放CGPDFDocumentRef。

当OP添加代码后进行编辑:

您是否使用CGPDFDocumentGetPage()获取pdfPage?如果是这样,您不应该释放它,因为它是一个自动释放的对象。

关于如何将其添加为子层:

您实际上不需要TiledPDFView。在您的视图控制器中,您可以简单地执行以下操作:

CATiledLayer *tiledLayer = [CATiledLayer layer];
tiledLayer.delegate = self; //sets where tiledLayer will look for drawLayer:inContext:
tiledLayer.tileSize = CGSizeMake(512.0f, 512.0f);
tiledLayer.levelsOfDetail = 4;
tiledLayer.levelsOfDetailBias = 4;
tiledLayer.frame = CGRectIntegral(CGRectMake(0.0f, 0.0f, 512.0f, 512.0f));
[self.view.layer addSublayer:tiledLayer];

将您的drawLayer:inContext:实现移动到视图控制器中。

然后在视图控制器的dealloc中,释放它:

[tiledLayer removeFromSuperlayer];
tiledLayer.delegate = nil;
CGPDFDocumentRelease(pdf);

请注意,您无法在UIView子类上执行此操作,因为drawLayer:inContext:将与UIView的主层发生冲突。

仍然没有帮助。在我的视图上(使用CATiledLayer),我有一个方法 -(void) stopDrawing { [tiledLayer removeFromSuperlayer]; tiledLayer.delegate = nil; } 我调用这个方法,然后释放CGPDFDocumentRef... - negersiu
你什么时候调用-stopDrawing?我假设你在-dealloc中释放了CGPDFDocumentRef。此外,CATiledLayer是您视图的主层还是子层?我的解决方案仅在将CATiledLayer作为子层而不是主层进行测试。我建议您将CATiledLayer添加为子层,而不是替换视图的主层。 - Altealice
如果这仍然没有帮助,您介意在您的帖子中添加更多代码以提供帮助吗?例如,您初始化CGPDFDocumentRef的位置,您添加CATiledLayer的位置和方式,您的-drawLayer:inContext:方法以及您释放CGPDFDocumentRef的位置。 - Altealice
CATiledLayer是主图层。我编辑了我的帖子,提供了一些代码。如何将CATiledLayer变成一个子图层,以便drawLayer:inContext方法会在该子图层上执行? - negersiu

3

对象.layer.contents = Nil

这应该等待线程完成。这在我的情况下很有帮助。


1
我之前也遇到过类似的问题。 最终我在TiledPDFView中设置了一个浮点变量"zoom",并在UIScrollview Delegate方法:scrollViewDidZoom中将其设置为PDFScrollView的zoomScale。
然后在我的TiledPDFView内的drawLayer方法中,只有当浮点变量“zoom”大于2时才调用该方法的内容。 这样可以解决任何未进行缩放就离开视图的问题。如果有人缩放超过2然后快速释放视图控制器,这种方法可能不是很理想,但是你可能能够找到类似的技术来涵盖所有情况。

0

看起来你正在做和我一样的事情,就是借用ZoomingPDFView的代码并将其集成到你的项目中。如果你的PDFScrollView中的UIScrollView代理方法没有改变,那么只需注释掉TiledPDFView中dealloc方法的两行代码即可解决问题。那些东西本来只会在你杀死父视图时发生。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接