【问题标题】:Memory management in Coregraphics (iOS)Coregraphics (iOS) 中的内存管理
【发布时间】:2023-03-14 09:57:01
【问题描述】:

我正在开发一个绘图应用程序,我正在使用 CGlayers 进行绘图,所以我打开我的画布以在单击按钮时进行绘图,

我正在使用 UIBezierPath,然后在下面的 touchesMoved 中将其转换为 CGPath,然后使用它来绘制

 -(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;
        }

    }

这是我的 drawRect 方法

- (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;

    }

我手动创建了setter方法

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

现在当用户点击保存按钮时,我会以这种方式从画布中取出图像

-(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];


}

我得到图像并在屏幕上的网格上显示它并调整大小的图像,但是当我执行这些操作时,我的内存使用量总是飙升,并且没有减少,这是创建三张图纸后的屏幕截图

要调整图像大小,我使用它,我在单独的线程中运行它

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();
});

内存不断增加,最后在创建了大约 30 个绘图后,我的应用程序崩溃了,因为内存峰值高达 550MB

所以,我不明白哪里出了问题,如何在绘图时管理内存问题。

【问题讨论】:

  • 你有没有运行泄漏工具来查看内存的去向?
  • 是的,它显示在 CGLayerCreateWithContext(context, bounds.size, NULL);并且在 touchesMoved 这一行 "mutablePath = CGPathCreateMutableCopy(cgPath); "

标签: ios core-graphics performselector cglayer


【解决方案1】:

您的mutablePath 泄漏,因为您在-drawRect: 中释放了前一个(并且仅当m_drawStep == DRAW 时)。正如-setNeedDisplay 只是将视图标记为需要重绘。 -drawRect: 仅在下一个绘图周期发送,因此在绘图周期之间可能会附加几个 -touchesMoved:withEvent:。 还有一些泄漏,当m_drawStep != DRAW 时可能会发生这种情况。

所以从-drawRect:方法中删除CGPathRelease(mutablePath)并在-touchesMoved:withEvent:中释放它

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

[self setNeedsDisplay];

同时释放您在-setCurrentDrawingLayer: 中设置后创建的CGLayerRef,因为您将其保留在-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...
}

【讨论】:

  • 您好,感谢您的回答,我在 touchesMoved 中执行了 CGPathRelease(mutablePath);它可以工作,但我仍然在“CGLayerRef layer = CGLayerCreateWithContext(context, bounds.size, NULL);”中出现泄漏在尝试了你的答案之后。
  • 内存峰值仍在继续
  • @Ranjit 你在别处保留CGLayerRef 吗? mutablePath 的泄漏已修复,因此如果内存峰值仍然持续,您需要使用更多信息编辑您的问题...
  • 我没有在别处保留它。performSelectorOnMainThread 会导致内存峰值
  • 据我所知,泄漏仪器能准确告诉你什么?
猜你喜欢
  • 1970-01-01
  • 2011-10-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多