I'm getting a BAD ACCESS error using [UIBezierPath CGPath]
with CAShapeLayer
under ARC. I've tried bridging in various ways but I'm not clear if that is the problem. I have isolated the crash to using the result of the makeToPath
method:
maskLayer = [CAShapeLayer layer];
maskLayer.path = [self makeToPath];
But this doesn't crash:
maskLayer = [CAShapeLayer layer];
maskLayer.path = [self makeFromPath];
Is there something invalid with the path created by makeToPath
? I'm planning to use the from and to paths with a CABasicAnimation
once I sort this crash out. What is the correct ARC bridging for CGPathRef
s from UIBezierPath
?
-(CGPathRef)makeToPath
{
UIBezierPath* triangle = [UIBezierPath bezierPath];
[triangle moveToPoint:CGPointZero];
[triangle addLineToPoint:CGPointMake(self.view.frame.size.width,0)];
[triangle addLineToPoint:CGPointMake(0, self.view.frame.size.height)];
[triangle closePath];
return [triangle CGPath];
}
-(CGPathRef)makeFromPath
{
UIBezierPath*rect = [UIBezierPath bezierPathWithRect:self.view.frame];
return [rect CGPath];
}
UPDATE So I changed my .h file per an answer below but I am still getting the crash
-(CGPathRef)makeToPath CF_RETURNS_RETAINED;
-(CGPathRef)makeFromPath CF_RETURNS_RETAINED;
I also tried making my methods return a UIBezierPath
instance per the answer here (shown below). Still no success. Anyone want to give me the longform explanation on how to fix this?
maskLayer.path = [[self makeToPath] CGPath];// CRASHES
morph.toValue = CFBridgingRelease([[self makeToPath] CGPath]);// CRASHES
-(UIBezierPath*)makeToPath
{
UIBezierPath* triangle = [UIBezierPath bezierPath];
[triangle moveToPoint:CGPointZero];
[triangle addLineToPoint:CGPointMake(self.view.frame.size.width,0)];
[triangle addLineToPoint:CGPointMake(0, self.view.frame.size.height)];
[triangle closePath];
return triangle;
}
The problem is with returning the CGPath. The value returned is a CGPathRef which is not covered by ARC. The UIBezierPath
you create is released after the method ends. Thus also freeing the CGPathRef.
You can specify a source annotation to let ARC know your intent:
In the .h file:
-(CGPathRef)makeToPath CF_RETURNS_RETAINED;
-(CGPathRef)makeFromPath CF_RETURNS_RETAINED;
As the other poster pointed out, you are returning a CGPath reference taken from a UIBezierPath object that goes out of scope at the end of the method. As the docs on the UIBezierPath CGPath property say:
The path object itself is owned by the UIBezierPath object and is valid only until you make further modifications to the path.
You need to create a copy of your CGPath and return that:
-(CGPathRef)makeToPath
{
UIBezierPath* triangle = [UIBezierPath bezierPath];
[triangle moveToPoint:CGPointZero];
[triangle addLineToPoint:CGPointMake(self.view.frame.size.width,0)];
[triangle addLineToPoint:CGPointMake(0, self.view.frame.size.height)];
[triangle closePath];
CGPathRef theCGPath = [triangle CGPath];
return CGPathCreateCopy(theCGPath);
}
The way I read the link to the llvm project, I think that the cf_returns_retained qualifier is intended to tell the caller the memory management policy for the returned value, rather than doing the retain for you.
Thus I think you would both need to create a copy of the path AND add the cf_returns_retained qualifier. I'm not clear on the syntax of that qualifier, however. (Never used it before.)
Assuming the other poster had the right syntax, it would look something like this:
-(CGPathRef)makeToPath CF_RETURNS_RETAINED;
{
UIBezierPath* triangle = [UIBezierPath bezierPath];
[triangle moveToPoint:CGPointZero];
[triangle addLineToPoint:CGPointMake(self.view.frame.size.width,0)];
[triangle addLineToPoint:CGPointMake(0, self.view.frame.size.height)];
[triangle closePath];
CGPathRef theCGPath = [triangle CGPath];
return CGPathCreateCopy(theCGPath);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With