0

我有一个与此类似的问题:

NSValue 中的 CGLayerRef - 何时调用 retain() 或 release()?

我在视图中绘制 24 个圆圈作为径向渐变。为了加快速度,我将渐变绘制到一个图层中,然后将该图层绘制 24 次。这非常有效地加快了渲染速度。在随后的 drawRect 调用中,一些圆圈可能需要用不同的色调重新绘制,而另一些则保持不变。

每次通过 drawRect 我都会用新的色调重新计算一个新的渐变并将其绘制到一个新图层中。然后我循环遍历圆圈,根据需要使用原始图层/渐变或新图层/渐变绘制它们。我有一个 24 个元素的 NSMutableArray,它为每个圆圈存储一个 CGLayerRef。

我认为这是我在上面链接的问题中提供的答案,但它对我不起作用。第二次通过 drawRect 时,使用存储在数组中的 CGLayerRef 绘制的任何圆都会导致程序在调用 CGContextDrawLayerAtPoint 时崩溃。在调试器中,我验证了原始 CGLayerRef 的实际十六进制值已正确存储到数组中,并且第二次通过 drawRect 将相同的十六进制值传递给 CGContextDrawLayerAtPoint。

此外,我发现如果我不 CGLayerRelease 层,那么程序不会崩溃,它工作正常。这告诉我层的内存管理出了问题。我的理解是将对象存储到 NSArray 中会增加它的引用计数,并且在数组释放它之前不会释放它。

无论如何,这里是drawRect的相关代码。在底部你可以看到我注释掉了 CGLayerRelease。在此配置中,应用程序不会崩溃,尽管我认为这是资源泄漏。如果我取消注释该版本,则应用程序会第二次崩溃,但 drawRect (在第一次和第二次调用之间,其中一个圆圈的 led_info.selected 属性已清除,表明它应该使用保存的图层而不是新图层:

NSLog(@"ledView drawing hue=%4f sat=%4f num=%d size=%d",hue_slider_value,sat_slider_value,self.num_leds,self.led_size);
rgb_color = [UIColor colorWithHue:1.0 saturation:1.0 brightness:1.0 alpha:1.0];
end_color = [UIColor colorWithHue:1.0 saturation:1.0 brightness:1.0 alpha:0.0];
NSArray *colors = [NSArray arrayWithObjects:
                   (id)rgb_color.CGColor, (id)end_color.CGColor, nil];
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace,(__bridge CFArrayRef) colors, NULL);

CGLayerRef layer = CGLayerCreateWithContext(context, (CGSize){self.led_size,self.led_size}, /*auxiliaryInfo*/ NULL);
if (layer) {
    CGContextRef layer_context = CGLayerGetContext(layer);
    CGContextDrawRadialGradient(layer_context, gradient, led_ctr,self.led_size/8,led_ctr, self.led_size/2,kCGGradientDrawsBeforeStartLocation);
} else {
    NSLog(@"didn't get a layer");
}

for (int led=0;led<[self.led_info_array count];led++) {
    led_info=[self.led_info_array objectAtIndex:led];

// the first time through selected=1 and led_info.cg_layer=nil for all circles,
// so this branch is taken.
    if (led_info.selected || led_info.cg_layer==nil) {
        CGPoint startPoint=led_info.rect.origin;
        CGContextDrawLayerAtPoint(context, startPoint, layer);
        CGContextAddRect(context, led_info.rect);
        led_info.cg_layer=layer;

// the second time through drawRect one or more circles have been deselected.
// They take this path through the if/else
    } else {
        CGPoint startPoint=led_info.rect.origin;
// app crashes on this call to CGContextDrawLayerAtPoint
        CGContextDrawLayerAtPoint(context, startPoint, led_info.cg_layer);
    }

}

// with this commented out the app doesn't crash.
//CGLayerRelease(layer);

这是led_info的声明:

@interface ledInfo : NSObject
@property CGFloat hue;
@property CGFloat saturation;
@property CGFloat brightness;
@property int selected;
@property CGRect rect;
@property CGPoint center;
@property unsigned index;
@property CGLayerRef cg_layer;
- (NSString *)description;
@end

led_info_array 是 ledInfo 对象的 NSMutableArray,数组本身是视图的一个属性:

@interface ledView : UIView
@property float hue_slider_value;
@property float sat_slider_value;
@property unsigned num_leds;
@property unsigned led_size;
@property unsigned init_has_been_done;
@property NSMutableArray *led_info_array;
//@property layerPool *layer_pool;
@end

数组初始化如下: self.led_info_array = [[NSMutableArray alloc] init];

编辑:自从我发布以来,我发现如果我将分配周围的保留/释放放入 NSMutableArray 中,那么我也可以保留原始 CGLayerRelease 并且应用程序可以工作。所以我想这就是它应该如何工作,虽然我想知道为什么保留/释放是必要的。在我正在阅读的目标 C 书籍(以及上面链接的问题的答案)中,我认为隐式分配给 NSArray 确实保留/释放。新的工作代码如下所示:

    if (led_info.selected || led_info.cg_layer==nil) {
        CGPoint startPoint=led_info.rect.origin;
        CGContextDrawLayerAtPoint(context, startPoint, layer);
        CGContextAddRect(context, led_info.rect);
        if (led_info.cg_layer) CGLayerRelease(led_info.cg_layer);
        led_info.cg_layer=layer;
        CGLayerRetain(layer);
    } else {
        CGPoint startPoint=led_info.rect.origin;
        CGContextDrawLayerAtPoint(context, startPoint, led_info.cg_layer);
    }

您可能会说我是 Objective C 和 iOS 编程的新手,并且我意识到我并没有真正遵守关于大小写和其他事情的约定。我会清理它,但现在我想解决这个内存管理问题。

罗布,谢谢你的帮助。我可以进一步澄清一下。我认为从你所说的有两个问题:

1) 引用计数不适用于 CGLayerRef。好的,但是在编写代码而不是在调试之后知道这一点会很好。我在 Objective C/cocoa 中使用“事物”时资源计数不起作用的迹象是什么?

2)你说我存储到一个属性,而不是一个 NSArray。是的,但是存储的目的地是 NSArray 通过属性,它是一个指针。该值确实使其进入数组并退出。资源计数不是这样工作的吗?即,如果我使用上面的代码将一些 NSObject 存储到 NSArray 中,而不是 CGLayerRef,资源计数会起作用吗?如果不是,那么摆脱中间的 led_info 属性并直接从循环内访问数组会起作用吗?

4

1 回答 1

0
于 2012-11-27T07:19:22.590 回答