绘制先前准备好的CGPath时,CATiledLayer崩溃

Pio*_*ach 1 iphone cgpath ipad catiledlayer ios

我使用CATiledLayer作为我的UIView的支持层,我把它放在UIScrollView中.在我的视图的init方法中,我正在创建绘制简单线条的CGPathRef对象.当我尝试在drawLayer:inContext中绘制此路径时,当我滚动/缩放时,它偶尔会与EXEC_BAD_ACCESS(很少)崩溃.

代码很简单,我只使用标准的CG*函数:

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        CATiledLayer *tiledLayer = (CATiledLayer *)[self layer];
        tiledLayer.levelsOfDetail = 10;
        tiledLayer.levelsOfDetailBias = 5;
        tiledLayer.tileSize = CGSizeMake(512.0, 512.0);

        CGMutablePathRef mutablePath = CGPathCreateMutable();
        CGPathMoveToPoint(mutablePath, nil, 0, 0);
        CGPathAddLineToPoint(mutablePath, nil, 700, 700);
        path = CGPathCreateCopy(mutablePath);
        CGPathRelease(mutablePath);
    }
    return self;
}

+ (Class) layerClass {
    return [CATiledLayer class];
}

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

- (void) drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {
    CGContextSetRGBFillColor(ctx, 1, 1, 1, 1);
    CGContextFillRect(ctx, self.bounds);

    CGContextSetLineWidth(ctx, 5);

    CGContextAddPath(ctx, path);
    CGContextDrawPath(ctx, kCGPathStroke);
}

- (void)dealloc {
    [super dealloc];
}
Run Code Online (Sandbox Code Playgroud)

更新:我注意到这个问题仅存在于iOS 5上,它在4.3上工作正常

Nic*_*anu 5

尝试在自定义MKOverlayView上绘制缓存的CGPath对象时遇到了类似的问题.

崩溃可能是因为无法在多个线程上同时绘制CGPath - 它是一个不透明的类(如文档中所指定)包含指向其points数组中当前点的指针.两个或多个线程在它们绘制时同时迭代这个数组可能导致未定义的行为和崩溃.

我通过将CGPath对象复制到每个绘图线程(包含在互斥锁中以防止不完整的复制)来解决这个问题:

//lock the object's cached data
pthread_mutex_lock(&cachedPathMutex);
//get a handle on the previously-generated CGPath (myObject exists on the main thread)
CGPathRef myPath = CGPathCreateCopy(myObject.cachedPath);
//unlock the mutex once the copy finishes
pthread_mutex_unlock(&cachedPathMutex);

// all drawing code here
CGContextAddPath(context, myPath);
...
...
CGPathRelease(myPath);
Run Code Online (Sandbox Code Playgroud)

如果您担心在每个线程上执行复制的内存开销,您也可以直接在缓存的CGPath对象上工作,但是在整个绘制过程中互斥锁必须保持锁定(哪种类型会破坏线程的目的)画画):

//lock the object's cached data
pthread_mutex_lock(&cachedPathMutex);

//get a handle on the previously-generated CGPath (myObject exists on the main thread)
CGPathRef myPath = myObject.cachedPath;

// draw the path in the current context
CGContextAddPath(context, myPath);
...
...

//and unlock the mutex
pthread_mutex_unlock(&cachedPathMutex);
Run Code Online (Sandbox Code Playgroud)

我将通过说我不是Quartz的多线程绘图专家来限定我的答案,只是这种方法解决了我的场景中的崩溃问题.祝好运!

更新:我现在重新访问此代码,因为iOS 5.1.0已经出来,看起来问题的根本原因可能实际上是iOS 5.0.x中的Quartz中的一个错误.在iOS 5.1.0上测试并删除了CGPathCreateCopy()和互斥锁调用时,我没有看到iOS 5.0.x上遇到的任何崩溃.

//get a handle on the previously-generated CGPath (myObject exists on the main thread)
CGPathRef myPath = myObject.cachedPath;

// all drawing code here
CGContextAddPath(context, myPath);
...
...
//drawing finished
Run Code Online (Sandbox Code Playgroud)

由于我们有机会支持iOS 5.0.x一段时间,因此将代码中的互斥锁保留在您的代码中(除了轻微的性能影响之外)或者只是在绘制之前运行版本检查并不会有什么坏处.