菜鸟教程小白 发表于 2022-12-12 23:52:31

ios - 使用 CGPathCreateCopyByTransforming 和 CGPathCreateCopy 导致的挤压内存泄漏


                                            <p><p>我正在使用 ARC 和 while 循环来调整几个 CGPathRef,直到它们符合我的某些约束条件,并且一旦满足 while 循环的条件,我就需要访问路径。</p>

<p>谁能解释我的代码是如何泄漏的? </p>

<p>CGCreateCopy... 函数的文档说它们会创建一个新副本,您负责发布该副本。所以我相信我的代码路径(while 循环的结尾)确实释放了 Instruments 正在捕获并将泄漏对象归因于的路径引用。</p>

<p>Instrument 调用在下面的代码中表示,并在 Instruments 突出显示的位置下方带有注释。</p>

<p>这个例程被赋予一个 GCSize 大小,它是一个矩形的大小和一个 int 值,它是一个最终将在此处创建的形状中显示的数值(形状的尺寸在某种程度上取决于字符串的大小这个值的表示)</p>

<p>我应该注意,代码确实会生成我想要的所需路径——我不是要求任何人调试这个 block !但它泄漏了,我不明白为什么。</p>

<pre><code>float lineThickness = size.width;
float perimeterStrokeWidth = lineThickness * 2;
CGSize shadowOffset = CGSizeMake( perimeterStrokeWidth / 1.5, perimeterStrokeWidth * 1.25 );
float shadowBlur = perimeterStrokeWidth * 2.0;

NSString *edgeSymbol = @&#34;°&#34;;
CGPathRef rightEdgePath = NULL;
CGPathRef leftEdgePath = NULL;
CGPathRef valueStringPath = NULL;
CGAffineTransform adjust, flip;
float pointsize, deltaX, deltaY, inset, pointSizeThatFits;
CGRect valueRect, edgeRect, leftEdgeRect, rectForValue;

CGSize trialSize = size;
pointsize = size.height;
CGPoint tdc; // top dead center
CGPoint bdc; // bottom dead center

NSLog(@&#34;\n\n&#34;);
BOOL done = false;
int attempts = 0;
int attemptsRemaining = 11; // ensure no infinite looping

while ( ! done ) {
    done = YES; // presumptive close!tests below will reset if they fail
    --attemptsRemaining;

    trialSize = CGSizeMake(trialSize.width, trialSize.height - attempts);
    ++attempts;

    lineThickness = trialSize.height / 60;
    perimeterStrokeWidth = lineThickness * 2;
    shadowOffset = CGSizeMake( perimeterStrokeWidth / 1.5, perimeterStrokeWidth * 1.0 );
    shadowBlur = perimeterStrokeWidth * 2.0;

    pointsize = [UIFont sizeFont:[UIFont fontWithName:@&#34;Alameda&#34;
                                                 size:pointsize]
                     toFitText:edgeSymbol
                      withinRect:CGRectMake(0,
                                          0,
                                          trialSize.width,
                                          trialSize.height)];
    // sizeFont:toFitText:withinRect: is a custom addition to UIFont that
    // recursively tries smaller and smaller pointsize values until the
    // text fits within the rectangle provided.

    CGPathRef tmpRightEdgePath = ];
    // newPathWithFont: is a custom addition to NSString that returns a
    // CGPathRef representing the glyphs that make up the string, rendered in
    // the font (and size) specified

    flip = CGAffineTransformMakeScale(-1.0, 1.0);

    CGPathRef tmpLeftEdgePath = CGPathCreateCopyByTransformingPath(tmpRightEdgePath, &amp;flip);

    edgeRect = CGPathGetBoundingBox(tmpRightEdgePath);

    // Center the symbol vertically in the size we were given (maybe up a little because of the shadow below?)

    deltaY = ( (size.height / 2) - (edgeRect.size.height / 2) - edgeRect.origin.y);

    // Slide it over to the right edge, inset slightly for the shadow
    inset = shadowOffset.width + shadowBlur;
    deltaX = ( trialSize.width - edgeRect.origin.x - edgeRect.size.width - inset);

    adjust = CGAffineTransformMakeTranslation(deltaX, deltaY);
    CGPathRef tmpRightEdgePath1 = CGPathCreateCopyByTransformingPath(tmpRightEdgePath, &amp;adjust);
    CGPathRelease(tmpRightEdgePath);
    tmpRightEdgePath = NULL;
    edgeRect = CGPathGetBoundingBox(tmpRightEdgePath1);

    leftEdgeRect = CGPathGetBoundingBox(tmpLeftEdgePath);
    deltaX = inset - leftEdgeRect.origin.x;
    adjust = CGAffineTransformMakeTranslation(deltaX, deltaY);
    CGPathRef tmpLeftEdgePath1 = CGPathCreateCopyByTransformingPath(tmpLeftEdgePath, &amp;adjust);
    CGPathRelease(tmpLeftEdgePath);
    tmpLeftEdgePath = NULL;

    leftEdgeRect = CGPathGetBoundingBox(tmpLeftEdgePath1);

    rectForValue = CGRectMake(leftEdgeRect.origin.x + leftEdgeRect.size.width,
                                     leftEdgeRect.origin.y,
                                     edgeRect.origin.x - leftEdgeRect.origin.x - leftEdgeRect.size.width,
                                     leftEdgeRect.size.height);

    NSString *valueString = ;
    pointSizeThatFits =
                               toFitText:valueString
                              withinRect:rectForValue];
    valueStringPath = ];

    valueRect = CGPathGetBoundingBox(valueStringPath);
    deltaY = ( (size.height / 2) - (valueRect.size.height / 2) - valueRect.origin.y);
    deltaX = ( (trialSize.width / 2)- (valueRect.size.width / 2)- valueRect.origin.x);
    adjust = CGAffineTransformMakeTranslation(deltaX, deltaY);
    CGPathRef tmpValueStringPath1 = CGPathCreateCopyByTransformingPath(valueStringPath, &amp;adjust);
    CGPathRelease(valueStringPath);
    valueStringPath = NULL;

    valueRect = CGPathGetBoundingBox(tmpValueStringPath1);

    CGPathRef tmpRightEdgePath2;
    CGPathRef tmpLeftEdgePath2;

    if ((valueRect.origin.x + valueRect.size.width) &lt; edgeRect.origin.x) {
      float gapToClose = edgeRect.origin.x - (valueRect.origin.x + valueRect.size.width);
      adjust = CGAffineTransformMakeTranslation(- gapToClose, 0);
      {
            tmpRightEdgePath2 = CGPathCreateCopyByTransformingPath(tmpRightEdgePath1, &amp;adjust);
            // ^^^^ Instruments reports 5.9% of leaks here
            CGPathRelease(tmpRightEdgePath1);
            tmpRightEdgePath1 = NULL;
      }

      adjust = CGAffineTransformMakeTranslation(gapToClose, 0);
      {
            tmpLeftEdgePath2 = CGPathCreateCopyByTransformingPath(tmpLeftEdgePath1, &amp;adjust);
            // ^^^^ Instruments reports 23.5% of leaks here
            CGPathRelease(tmpLeftEdgePath1);
            tmpLeftEdgePath1 = NULL;
      }

      leftEdgeRect = CGPathGetBoundingBox(tmpLeftEdgePath2);
    } else {
      {
            tmpRightEdgePath2 = CGPathCreateCopy(tmpRightEdgePath1);
            CGPathRelease(tmpRightEdgePath1);
            tmpRightEdgePath1 = NULL;
      }
      {
            tmpLeftEdgePath2= CGPathCreateCopy(tmpLeftEdgePath1);
            CGPathRelease(tmpLeftEdgePath1);
            tmpLeftEdgePath1 = NULL;
      }
      leftEdgeRect = CGPathGetBoundingBox(tmpLeftEdgePath2);
    }

    tdc.x = CGRectGetMidX(rectForValue);
    bdc.x = tdc.x;

    tdc.y = leftEdgeRect.origin.y - (tdc.x - leftEdgeRect.origin.x) * 0.08; // SWAG on the 10% of width

    if ((tdc.y - lineThickness/2.0) &lt; 0) {
      done = NO;
    }

    bdc.y = leftEdgeRect.origin.y + leftEdgeRect.size.height + (leftEdgeRect.origin.y - tdc.y);

    float shadowness = shadowOffset.height;
    if ((bdc.y + shadowness) &gt; size.height) {
      done = NO;
    }

    if (attemptsRemaining &lt;= 0) {
      done = YES;
    }

    // And finally, assign them out if DONE!

    if (done) {
      leftEdgePath = CGPathCreateCopy(tmpLeftEdgePath2);
      // ^^^^ Instruments reports 35.3% of leaks here
      rightEdgePath = CGPathCreateCopy(tmpRightEdgePath2);
      // ^^^^ Instruments reports 35.3% of leaks here
      valueStringPath = CGPathCreateCopy(tmpValueStringPath1);
    }
    CGPathRelease(tmpLeftEdgePath2);
    tmpLeftEdgePath2 = NULL;
    CGPathRelease(tmpRightEdgePath2);
    tmpRightEdgePath2 = NULL;
    CGPathRelease(tmpValueStringPath1);
    tmpValueStringPath1 = NULL;
}
// what follows is the application of those paths within CGContext drawing calls.
</code></pre>

<p>我从关于仪器和泄漏的其他有用答案中了解到,标记的行通常是创建即将泄漏的对象的位置。考虑到这一点,我向下看我可能会在没有首先释放先前路径的情况下分配新路径,但没有任何(我可以看到 - 但我承认我有点睡眼惺忪看着这个)。</p></p>
                                    <br><hr><h1><strong>Best Answer-推荐答案</ strong></h1><br>
                                            <p><p>为了其他发现自己被内存泄漏问题困扰的人的利益,我将在此处提供我的问题的答案,因为侦查过程可能会对您有所帮助。</p>

<p>在代码 list 的中间,您可以看到我在哪里通知编译器我将很快使用两个 CGPathRef,最初在 if/then/else 的范围内,所以我需要在该 block 之外分配(?)所以它们在我的日常生活中是本地的。</p>

<pre><code>CGPathRef tmpRightEdgePath2;
CGPathRef tmpLeftEdgePath2;
</code></pre>

<p>通过将它们更改为:</p>

<pre><code>CGPathRef tmpRightEdgePath2 = NULL;
CGPathRef tmpLeftEdgePath2 = NULL;
</code></pre>

<p>这清除了在 if/then/else 语句期间报告的两个泄漏。</p>

<p>所以现在我剩下的两个泄漏报告在 if (done) {}block 内。</p>

<p>在 Instruments 中,查看 Leaks 工具,Cycles & Roots -> Leak Cycles -> foo History 我看到 10 个内存事件以 RefCt 为 1 结束,因此很明显是泄漏:</p>

<pre><code>#Category         Event Type    RefCtResponsibleLibraryResponsible Caller
0NSMutableRLEArrayMalloc      1      Foundation          -
1NSMutableRLEArrayRelease       0      Foundation          -
2NSMutableRLEArrayFree          0      Foundation          -
3CGPath             Malloc      1      CoreGraphics      CGTypeCreateInstance
4CGPath             CFRelease   0      my app            my routine
5CGPath             Free          0      my app            my routine
6CGPath             Malloc      1      CoreGraphics      CGTypeCreateInstance
7CGPath             CFRelease   0      my app            my routine
8CGPath             Free          0      my app            my routine
9CGPath             Malloc      1      CoreGraphics      CGTypeCreateInstance
</code></pre>

<p>所以在我认为我已经完成了路径操作之后的某个时刻,必须发生另一个 CGCreateCopy... 样式调用,因为在我发布所有内容之后,<em></em> 调用了 Malloc。因此,通过使用搜索/查找,我通过绘图代码继续前进,并找到了最后一个 CGPathCreateCopyByTransforming:</p>

<pre><code>CGAffineTransform slightUp = CGAffineTransformMakeTranslation(0, - lineThickness * 2 );
leftEdgePath = CGPathCreateMutableCopyByTransformingPath(leftEdgePath, &amp;slightUp);
rightEdgePath = CGPathCreateMutableCopyByTransformingPath(rightEdgePath, &amp;slightUp);
</code></pre>

<p>由于保留计数在该复制期间增加了 1,因此可以保证泄漏。所以我把它们改成了:</p>

<pre><code>CGAffineTransform slightUp = CGAffineTransformMakeTranslation(0, - lineThickness * 2 );
CGMutablePathRef finalLeftEdgePath = CGPathCreateMutableCopyByTransformingPath(leftEdgePath, &amp;slightUp);
CGPathRelease(leftEdgePath);
CGMutablePathRef finalRightEdgePath = CGPathCreateMutableCopyByTransformingPath(rightEdgePath, &amp;slightUp);
CGPathRelease(rightEdgePath);
</code></pre>

<p>然后我更改了我的 CGContextAddPath() 以匹配新的“最终”路径,以及最终发布调用以发布“最终”版本并且我没有泄漏!</p></p>
                                   
                                                <p style="font-size: 20px;">关于ios - 使用 CGPathCreateCopyByTransforming 和 CGPathCreateCopy 导致的挤压内存泄漏,我们在Stack Overflow上找到一个类似的问题:
                                                        <a href="https://stackoverflow.com/questions/13601239/" rel="noreferrer noopener nofollow" style="color: red;">
                                                                https://stackoverflow.com/questions/13601239/
                                                        </a>
                                                </p>
                                       
页: [1]
查看完整版本: ios - 使用 CGPathCreateCopyByTransforming 和 CGPathCreateCopy 导致的挤压内存泄漏