Search code examples
iosmemory-managementmemory-leakscore-graphicscore-animation

Memory issues with Core Graphics


I draw a table "by hand", by using primitives of Core Graphics. The view is re-draw when I click a button. The problem is that when I profile the code in Instruments, the VM Regions keep increase, while Heap and Anonymous VM oscillate (and I would expect it).

enter image description here

And the details about CoreAnimation:

enter image description here

An excerpt of my drawRect:

- (void)drawRect:(CGRect)rect
{

    // Drawing code
    CGContextRef context     = UIGraphicsGetCurrentContext();

    CGRect paperRect         = CGRectMake(self.bounds.origin.x+hourLabelSize+self.marginX,
                                           10 +self.cellBorder  ,
                                          self.bounds.size.width,
                                          self.cellHeight * 48
                                          );
    // custom shadow
    drawLinearGradient(context, paperRect, whiteColor.CGColor, lightGrayColor.CGColor);
    CGContextSaveGState(context);
    CGFloat outerMargin = 5.0f;
    CGFloat eventSize;
    // Draw table
    CGRect hourRect;
    CGRect outerRect;
    CGMutablePathRef outerPath;
    CGRect strokeRect;
    CGRect rowRect;
    for (int j=0; j < 7;j++)
    {
    for (int i=0; i < numberOfRows; i++)
    {
        // Odd index means we are in the half of an hour


        if ( (i%2) == 0)
        {
            [hour setString:[NSString stringWithFormat:@"%d:00",(int)floor(i/2)]];
            // Draw box around hours //
            if (j == 0 && i >0)
            {
                CGContextSaveGState(context);

                hourRect   = CGRectMake(5, i*cellHeight-5, hourLabelSize, 28);
                outerRect  = CGRectInset(hourRect, outerMargin, outerMargin);
                outerPath = newRoundedRectForRect(outerRect, 6.0);
                CGContextAddPath(context, outerPath);
                CFRelease(outerPath); // <--- This solve the leak!!

                // Draw gradient
                strokeRect = CGRectInset(hourRect, 5.0, 5.0);
                CGContextSetStrokeColorWithColor(context, boxColor.CGColor);
                CGContextSetLineWidth(context, 1.0);
                CGContextStrokeRect(context, strokeRect);
                drawLinearGradient(context, strokeRect, whiteColor.CGColor, lightGrayColor.CGColor);
                CGContextRestoreGState(context);

            }
        }
        else
        {
            [hour setString:[NSString stringWithFormat:@"%d:30",(int)floor(i/2)]];

        }

        // Draw hours
        if (j == 0 && i > 0)
            [hour drawInRect:CGRectMake(0, i*cellHeight, hourLabelSize+10, 26) withFont:font lineBreakMode:NSLineBreakByClipping alignment:NSTextAlignmentCenter];
        // Draw row
        CGContextBeginPath(context);
        CGContextSetStrokeColorWithColor(context, boxColor.CGColor);
        CGContextSetLineWidth(context, self.cellBorder);
        rowRect   = CGRectMake(j*cellWidth + hourLabelSize +self. marginX - self.cellBorder/2, i*cellHeight+10 + self.cellBorder / 2, cellWidth , cellHeight);
        CGContextStrokeRect(context, rowRect);


} //


    CGContextFlush(context);
    CGContextRestoreGState(context);

The first thing I do not understand is why I see VM:CoreAnimation. CoreGraphics is part of Core Animation? Secondly, what I shall watch as syntoms of bad allocation: VM Regions, XCode measurements or Head and Anonymous? Regards!

[EDIT]

The createRoundedRect is as follows

CGMutablePathRef newRoundedRectForRect(CGRect rect, CGFloat radius)
{
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathMoveToPoint(path, NULL, CGRectGetMidX(rect), CGRectGetMinY(rect));
    CGPathAddArcToPoint(path, NULL, CGRectGetMaxX(rect), CGRectGetMinY(rect), CGRectGetMaxX(rect), CGRectGetMaxY(rect), radius);
    CGPathAddArcToPoint(path, NULL, CGRectGetMaxX(rect), CGRectGetMaxY(rect), CGRectGetMinX(rect), CGRectGetMaxY(rect), radius);
    CGPathAddArcToPoint(path, NULL, CGRectGetMinX(rect), CGRectGetMaxY(rect), CGRectGetMinX(rect), CGRectGetMinY(rect), radius);
    CGPathAddArcToPoint(path, NULL, CGRectGetMinX(rect), CGRectGetMinY(rect), CGRectGetMaxX(rect), CGRectGetMinY(rect), radius);
    CGPathCloseSubpath(path);


    return path;
}:

and the drawLineGradient.m :

void drawLinearGradient(CGContextRef context, CGRect rect, CGColorRef startColor, CGColorRef endColor)
{
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGFloat locations[] = { 0.0, 1.0 };

    NSArray *colors = @[(__bridge id) startColor, (__bridge id) endColor];

    CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef) colors, locations);

CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));

CGContextSaveGState(context);
CGContextAddRect(context, rect);
CGContextClip(context);
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
CGContextRestoreGState(context);

CGGradientRelease(gradient);
CGColorSpaceRelease(colorSpace);
colors = nil;

}


Solution

  • The reason you see core animation is because all iOS views are layer-backed views. The reason for your leak is that you release the path. ARC does not manage Core Foundation objects.