Memory management in Coregraphics (iOS)

I am working on a drawing application, I am using CGlayers for drawing, So I open my canvas for drawing on click of a button,

I am using UIBezierPath and then converting it to CGPath in touchesMoved below and then using it to draw

  • How to reproduce this Xcode blue drag line
  • Drawing in a background thread on iOS
  • How can I edit PDF files in an iOS application?
  • Getting a CGImage from CIImage
  • Union UIBezierPaths rather than apend path
  • Can't add a corner radius and a shadow
  •  -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
        {           
            if (ctr == 4)
            {
                m_touchMoved = true;
    
                self.currentPath = [[DrawingPath alloc] init];
    
                [self.currentPath setPathColor:self.lineColor];
                self.currentPath.pathWidth = [NSString stringWithFormat:@"%f",self.lineWidth];
    
    
                 pts[3] = midPoint(pts[2], pts[4]);// move the endpoint to the middle of the line joining the second control point of the first Bezier segment and the first control point of the second Bezier segment
                [self.currentPath.path moveToPoint:pts[0]];
                [self.currentPath.path addCurveToPoint:pts[3] controlPoint1:pts[1] controlPoint2:pts[2]];
    
                CGPathRef cgPath = self.currentPath.path.CGPath;
                mutablePath = CGPathCreateMutableCopy(cgPath);        
    
                [self setNeedsDisplay];
    
                pts[0] = pts[3];
                pts[1] = pts[4];
                ctr = 1;
            }
    
        }
    

    This is my drawRect method

    - (void)drawRect:(CGRect)rect
        {
           switch (m_drawStep)
           {
               case DRAW:
               {
    
                   CGContextRef context = UIGraphicsGetCurrentContext();//Get a reference to current context(The context to draw)
    
    
                   if(currentDrawingLayer == nil)
                   {
                       CGFloat scale = self.contentScaleFactor;
                       CGRect bounds = CGRectMake(0, 0, self.bounds.size.width * scale, self.bounds.size.height * scale);
                       CGLayerRef layer = CGLayerCreateWithContext(context, bounds.size, NULL);
                       CGContextRef layerContext = CGLayerGetContext(layer);
                       CGContextScaleCTM(layerContext, scale, scale);
                       //currentDrawingLayer = layer;
                       [self setCurrentDrawingLayer:layer];
                       CGLayerRelease(currentDrawingLayer);
                   }
    
    
    
    
                   CGContextRef layerContext = CGLayerGetContext(currentDrawingLayer);
                   CGContextBeginPath(layerContext);
                   CGContextAddPath(layerContext, mutablePath);
                   CGContextSetLineWidth(layerContext, self.lineWidth);
                   CGContextSetLineCap(layerContext, kCGLineCapRound);
                   CGContextSetLineJoin(layerContext, kCGLineJoinRound);
                   CGContextSetAllowsAntialiasing(layerContext, YES);
                   CGContextSetShouldAntialias(layerContext, YES);
                   CGContextSetStrokeColorWithColor(layerContext, self.lineColor.CGColor);
                   CGContextSetFillColorWithColor(layerContext, self.lineColor.CGColor);
                   CGContextSetBlendMode(layerContext,kCGBlendModeNormal);
                   CGContextStrokePath(layerContext);
                   CGPathRelease(mutablePath);
    
                  CGContextDrawLayerInRect(context, self.bounds, currentDrawingLayer );
               }
                   break;
    
        }
    

    I have manually created setter method

    -(void)setCurrentDrawingLayer:(CGLayerRef)layer
    {
        CGLayerRetain(layer);
        CGLayerRelease(currentDrawingLayer);
        currentDrawingLayer = layer;
    }
    

    Now when user clicks on savebutton, I get image out of the canvas this way

    -(void)getImageFromCanvas
    {
        UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO,0.0);//Creates a bitmap based graphics context with
                                                                             //specified options
            [self.layer renderInContext:UIGraphicsGetCurrentContext()];//Renders the reciever and its layers into specified
                                                                       //context.
            m_curImage = UIGraphicsGetImageFromCurrentImageContext();  
            UIGraphicsEndImageContext();//Removes the current context from top of stack
    
         if(currentDrawingLayer)
         {
                CGLayerRelease(currentDrawingLayer);
         }
    
      [m_delegate performSelectorOnMainThread:@selector(getCanVasViewImage:)
                                     withObject:m_curImage
                                  waitUntilDone:NO];
    
    
    }
    

    I get the image and show it on a grid on the screen with resized image, But when I perform these operations, my memory usage always spikes up, and it is not decreasing,here is the screen shot after creating three drawings

    To resize image , I use this, I run this in a separate thread

    dispatch_async(dispatch_get_global_queue(0,0), ^{
        UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0);
    
         [image1 drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
    
    
         UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    
         UIGraphicsEndImageContext();
    });
    

    enter image description here

    memory goes on increasing, and finally after creating some 30 drawings, my app crashes, because memory spikes upto 550MB

    So, I am not understanding where I am going wrong, How I can manage memory issues while drawing.

    Solutions Collect From Internet About “Memory management in Coregraphics (iOS)”

    Your mutablePath leak, because you release the previous one in -drawRect: (and only if m_drawStep == DRAW). As -setNeedDisplay just marks the view as needing to be redrawn.
    -drawRect: is only send on the next drawing cycle, so several -touchesMoved:withEvent: may append between, drawing cycles.
    There is also some leak, that can happen when m_drawStep != DRAW.

    So remove CGPathRelease(mutablePath) from -drawRect: method and release it in -touchesMoved:withEvent:

    CGPathRef cgPath = self.currentPath.path.CGPath;
    CGPathRelease(mutablePath);
    mutablePath = CGPathCreateMutableCopy(cgPath);        
    
    [self setNeedsDisplay];
    

    Also release the CGLayerRef you create after setting it in -setCurrentDrawingLayer: because you retain it in -setCurrentDrawingLayer:.

    if(currentDrawingLayer == nil)
    {
        // ...
        CGLayerRef layer = CGLayerCreateWithContext(context, bounds.size, NULL); // layer is created, refCount == 1
        // ...
        [self setCurrentDrawingLayer:layer]; // layer is retained by -setCurrentDrawingLayer:, refCount == 2
         CGLayerRelease(layer); // release layer, refCount == 1
    
         // without it the previous release layer refCount still == 2, so in -setCurrentDrawingLayer:
         // CGLayerRelease(currentDrawingLayer) decrement refCount to 1 and leak...
    }