首页 文章

UIGraphicsGetCurrentContext()生命周期短

提问于
浏览
2

我有一个实现徒手画的视图,但我有一个小问题 . 我在iPad 3上注意到一切都变成了地狱,所以我试图更新我的绘图代码(可能就像我本来应该做的那样),只更新被描边的部分 . 但是,打开后的第一个行程和大约10秒的空转后的第一个行程非常慢 . 一切都是"warmed up"之后,它像黄油一样光滑,每个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 回答

  • 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秒,但我认为这是一个可接受的风险 .

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

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

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

相关问题