I have a class GraphicView
that basically creates a UIView
that uses a CAShapeLayer
as its default layer
object:
GraphicView.h
#import <UIKit/UIKit.h>
@interface GraphicView : UIView
{
CAShapeLayer *_shapeLayer;
}
@property(nonatomic, strong) CAShapeLayer *shapeLayer;
- (void) loadPath:(CGPathRef)path;
@end
GraphicView.m
#import "GraphicView.h"
@implementation GraphicView
- (id) initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
self.shapeLayer = (CAShapeLayer *) self.layer;
}
return self;
}
+ (Class)layerClass
{
return [CAShapeLayer class];
}
- (void) loadPath:(CGPathRef)path
{
CGMutablePathRef tempPath = path;
self.shapeLayer.path = tempPath;
CGPathRelease(tempPath);
}
@end
When I call loadPath
:
GraphicViewController.m
- (void) loadSquarePath
{
[self.graphicView loadPath:[SquarePath newSquarePath]];
}
SquareBalloonPath.m
- (CGMutablePathRef) newSquareBalloonPath
{
CGSize mutablePathInitialSize = [self squareBalloonPathInitialSize];
CGMutablePathRef mutablePath = CGPathCreateMutable();
CGPathMoveToPoint(mutablePath, NULL, 0, 0);
CGPathAddRect(mutablePath, NULL, CGRectMake(0, 0, mutablePathInitialSize.width, mutablePathInitialSize.height));
CGPathCloseSubpath(mutablePath);
return mutablePath;
}
I get an analyzer error saying "Potential leak of an object of type 'CGMutablePathRef'". However, when I move loadPath
from GraphicView
to GraphicViewController
(and change the method header to loadGraphicView:WithPath:
GraphicViewController.m
- (void) loadGraphicView:(GraphicView *)graphicView withPath:(CGPathRef)path
{
CGPathRef tempPath = path;
graphicView.shapeLayer.path = tempPath;
CGPathRelease(tempPath);
}
- (void) loadSquarePath
{
[self.graphicView loadPath:[SquarePath newSquarePath]];
}
The analyzer error goes away! Is there something I have to tell the compiler so I can get loadPath
to work when it's included as an instance method within GraphicView
?
The problem is with ownership. You have one place that creates and takes ownership of the CGMutablePathRef
. You then pass that reference to another method that then releases it. But that method should not take responsibility for releasing the reference. It has no idea if the reference it is getting should be released or not.
The simple fix is to put the responsibility in the correct place. Change loadSquarePath
in GraphicViewController
to something like this:
- (void) loadSquarePath
{
CGMutablePathRef path = [SquarePath newSquareBalloonPath];
[self.graphicView loadPath:path];
CGPathRelease(path);
}
and remove the call to CGPathRelease
from loadPath:
in GraphicView
.
Or in the second set of code, remove CGPathRelease
from loadGraphicView:withPath:
in GraphicViewController
.
These changes quiet the analyzer because it moves the responsibility of releasing to the same place that took the responsibility of ownership at creation.
This also eliminates possible crashes due to the CGReleasePath
in loadPath:
over-releasing paths that should not be released. While your current code is only passing a path reference that does need to be released, that may not always be the case.