I am using CATiledLayer as backing layer for my UIView, which I have put inside UIScrollView. In init method of my view I am creating CGPathRef object which draws simple line. When I am trying to draw this path inside drawLayer:inContext it occasionally crashes with EXEC_BAD_ACCESS (rarely) when I am scrolling / zooming.
The code is very simple, I am using only standard CG* functions:
- (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];
}
UPDATE:
I have noiced that this problem exists only on iOS 5, it works fine on 4.3
I ran into a similar issue when attempting to draw cached CGPath objects on a custom MKOverlayView.
The crash may occur because a CGPath can’t be simultaneously drawn on multiple threads – it’s an opaque class which (as specified in the documentation) contains a pointer to the current point in its points array. Two or more threads iterating over this array simultaneously while they draw it could lead to undefined behavior and a crash.
I worked around this by copying the CGPath object into each drawing thread (contained within a mutex lock to prevent incomplete copying):
If you’re concerned about the memory overhead of doing a copy on each thread, you can also work directly on the cached CGPath objects, but the mutex will have to remain locked during the whole drawing process (which kind of defeats the purpose of threaded drawing):
I’ll qualify my answer by saying that I’m not an expert on multithreaded drawing with Quartz, only that this approach solved the crashes in my scenario. Good luck!
UPDATE:
I revisited this code now that iOS 5.1.0 is out and it looks like the root cause of the issue may have actually been a bug in Quartz in iOS 5.0.x. When testing on iOS 5.1.0 with the CGPathCreateCopy() and mutex calls removed, I’m seeing none of the crashes experienced on iOS 5.0.x.
Since chances are we’ll be supporting iOS 5.0.x for a while, it won’t hurt to keep the mutex in your code (other than a slight performance hit), or simply run a version check before drawing.