UIGraphicsGetCurrentContext()生命周期短

时间:2012-06-25 09:31:37

标签: ios core-graphics drawrect ipad-3

我有一个实现徒手画的视图,但我有一个小问题。我在iPad 3上注意到一切都变成了地狱,所以我试图更新我的绘图代码(可能就像我本来应该做的那样),只更新被描边的部分。但是,打开后的第一个行程和大约10秒的空转后的第一个行程非常慢。在一切都“热身”之后,它就像黄油一样光滑,每次drawRect只需要大约0.15ms。我不知道为什么,但整个视图矩形被标记为第一个drawRect为脏,而第一个drawRect被标记为空闲(然后更新需要大约150毫秒)。堆栈跟踪显示我的矩形被CABackingStoreUpdate_

覆盖

如果矩形很大,我尝试不绘制图层,但是然后我的整个上下文变成空白(当我在旧区域上绘制时会重新出现,就像乐透彩票一样)。有没有人知道UIGraphicsGetCurrentContext()会发生什么?这是我能想象到的唯一麻烦的地方。也就是说,我的观点背景被上下文精灵所吸引,因此它需要再次完全呈现自己。我可以使用任何设置来保持相同的上下文吗?或者还有其他东西在这里......在初始显示后没有必要更新整个矩形。

我的drawRect非常简单:

- (void)drawRect:(CGRect)rect
{
    CGContextRef c = mDrawingLayer ? CGLayerGetContext(mDrawingLayer) : NULL;
    if(!mDrawingLayer)
    {
        c = UIGraphicsGetCurrentContext();
        mDrawingLayer = CGLayerCreateWithContext(c, self.bounds.size, NULL);
        c = CGLayerGetContext(mDrawingLayer);
        CGContextSetAllowsAntialiasing(c, true);
        CGContextSetShouldAntialias(c, true);
        CGContextSetLineCap(c, kCGLineCapRound);
        CGContextSetLineJoin(c, kCGLineJoinRound);
    }

    if(mClearFlag)
    {
        CGContextClearRect(c, self.bounds);
        mClearFlag = NO;
    }

    CGContextStrokePath(c);
    CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
    CGContextDrawLayerInRect(UIGraphicsGetCurrentContext(), self.bounds, mDrawingLayer);
    NSLog(@"%.2fms : %f x %f", (CFAbsoluteTimeGetCurrent() - startTime)*1000.f,  rect.size.width, rect.size.height);

}

1 个答案:

答案 0 :(得分:3)

我在Apple Dev Forums上找到了一个有用的线程来描述这个确切的问题。它只存在于iOS 5.0之后,理论上是因为Apple引入了双缓冲系统,所以前两个drawRects总是满的。但是,没有解释为什么在空闲之后会再次发生这种情况。理论上说,GPU不能保证底层缓冲区,并且会随心所欲地丢弃它,需要重新创建。解决方案(直到Apple发布某种真正的解决方案)是ping缓冲区以便它不会被释放:

mDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(pingRect)];
[mDisplayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

- (void)pingRect
{
    //Already drawing
    if(mTouchCount > 0) return;

    //Even touching just one pixel will keep the buffer alive
    [self setNeedsDisplayInRect:CGRectMake(0, 0, 1, 1)];
}

唯一的缺点是,如果用户手指完全静止超过5秒,但我认为这是一个可接受的风险。

编辑有趣的更新。事实证明,调用setNeedsDisplay足以使缓冲区保持活动状态,即使它立即返回。所以我把它添加到我的drawRect方法:

- (void)drawRect:(CGRect)rect
{
   if(rect.size.width == 1.f)
       return;
    //...
}

希望它能够抑制这种刷新方法肯定会增加的功耗。