Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CoreGraphics drawing causes memory warnings/crash on iOS 7

After updating my iPad (mini) to iOS7 I experienced that my drawing app is lagging and crashing after a few strokes.

Now, when I run the app with Instruments/memory allocation tool in xcode 5, I see that the VM: CG raster data category is filling up rapidly when drawing on the screen. There seems to be a multitude of CGDataProviderCreateWithCopyOfData calls being made, each 3.00Mb in size. After continous drawing the app receives memory warnings, and usually terminates.

The code basically strokes paths into an imagecontext, more or less like this:

UIGraphicsBeginImageContext(self.view.frame.size);
[drawImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), 5.0);
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 1.0, 0.0, 0.0, 1.0);
CGContextBeginPath(UIGraphicsGetCurrentContext());
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPoint.x, currentPoint.y);
CGContextStrokePath(UIGraphicsGetCurrentContext());
drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

On iOS7/iPad this is extremely laggy and has memory issues, while on iOS6 this was fairly speedy and had no negative memory footprint.

When I run this code in a non-retina iPhone version the CGDataProviderCreateWithCopyOfData calls are 604Kb in size, and only one or two are "active" at the same time. The drawing is fluent and fast with no memory warnings and no slowdown.

What happened from iOS6 to iOS7 regarding CoreGraphics and imagecontexts?

Sorry for any lingo errors or other possible stupid mistakes. Still a noob, doing iOS development in my spare time.

like image 936
machineboy Avatar asked Oct 03 '13 19:10

machineboy


2 Answers

I put my drawing code in an autoreleasepool .and this solved my problem.

Eg:-

@autoreleasepool {
    UIGraphicsBeginImageContext(self.view.frame.size);

    [drawImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];

    CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
    CGContextSetLineWidth(UIGraphicsGetCurrentContext(), 5.0);
    CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 1.0, 0.0, 0.0, 1.0);
    CGContextBeginPath(UIGraphicsGetCurrentContext());
    CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
    CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPoint.x, currentPoint.y);
    CGContextStrokePath(UIGraphicsGetCurrentContext());

    drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
}
like image 140
Priyanka V Avatar answered Dec 11 '22 09:12

Priyanka V


My solution in general was to create a Canvas UIView class that handles all drawing operations. I write to a cached CGImageRef and then combine the cache with the UIImage along the lines of this:

My custom drawRect method is something like this:

- (void)drawRect:(CGRect)rect
{
    // Drawing code
    UIGraphicsBeginImageContext(CGSizeMake(1024, 768));
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGImageRef cacheImage = CGBitmapContextCreateImage(cacheContext);
    CGContextDrawImage(context, self.bounds, cacheImage);

    // Combine cache with image
    drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
    CGImageRelease(cacheImage);
    UIGraphicsEndImageContext();
}

On touchesMoved I call a drawLine method that do some curve interpolation, brush size adjustment and end line tapering and then do a [self setNeedsDisplay];

This seems to be working well in iOS7. Sorry if I can't be more specific, but I'd rather not post actual production code from my app :)

like image 28
machineboy Avatar answered Dec 11 '22 08:12

machineboy