0

これに似た質問があります:

NSValueのCGLayerRef-retain()またはrelease()をいつ呼び出すか?

ビューに放射状のグラデーションとして24個の円を描画しています。それをスピードアップするために、グラデーションをレイヤーに描画してから、レイヤーを24回描画しています。これは、レンダリングを高速化するために非常にうまく機能しました。その後のdrawRect呼び出しで、一部の円を異なる色相で再描画する必要がある場合がありますが、他の円は同じままです。

drawRectを使用するたびに、新しい色相で新しいグラデーションを再計算し、それを新しいレイヤーに描画します。次に、円をループして、必要に応じて元のレイヤー/グラデーションまたは新しいレイヤー/グラデーションで円を描画します。各円のCGLayerRefを格納する24要素のNSMutableArrayがあります。

これは私が上でリンクした質問で提供された答えだと思いますが、それは私にとってはうまくいきません。2回目のdrawRectでは、配列に格納されたCGLayerRefを使用して描画された円により、CGContextDrawLayerAtPointを呼び出すときにプログラムがクラッシュします。デバッガーで、元のCGLayerRefの実際の16進値が配列に適切に格納されていることを確認し、2回目はdrawRectを介して同じ16進値がCGContextDrawLayerAtPointに渡されることを確認しました。

さらに、CGLayerReleaseレイヤーを使用しない場合、プログラムはクラッシュせず、正常に動作することがわかりました。これは、レイヤーのメモリ管理に問題があることを示しています。オブジェクトをNSArrayに格納すると、その参照カウントが増加し、配列がオブジェクトを解放するまで割り当てが解除されないことを理解しています。

とにかく、これがdrawRectからの関連コードです。一番下に、私がCGLayerReleaseをコメントアウトしたことがわかります。この構成では、これはリソースリークだと思いますが、アプリはクラッシュしません。そのリリースのコメントを外すと、drawRectを使用してもアプリが2回クラッシュします(1回目と2回目の呼び出しの間に、円の1つで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=[[NSMutableArrayalloc] init];

編集:投稿してから、assignemtの周りのretain / releaseをNSMutableArrayに入れると、元のCGLayerReleaseのままにして、アプリが機能することがわかりました。保持/解放が必要な理由を知りたいのですが、これがどのように機能するのかを推測します。私が読んでいるObjectiveCの本(および上記のリンクされた質問への回答)では、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);
    }

私はObjectiveCとiOSプログラミングにまったく慣れていないことがわかると思いますが、ケースやおそらく他のことに関して、私は実際には慣習に固執していないことに気づいています。これをクリーンアップしますが、今はこのメモリ管理の問題を解決したいと思います。

ロブ、助けてくれてありがとう。もう少し説明を使用できます。あなたが言っていることから、2つの問題があると思います。

1)参照カウントはCGLayerRefでは機能しません。OKですが、デバッグ後ではなく、コードの記述中にそれを知っておくと便利です。Objective C / cocoaで「もの」を使用すると、リソースのカウントが機能しないという私の兆候は何ですか?

2)NSArrayではなく、プロパティに格納しているとのことです。本当ですが、ストアの宛先は、ポインターであるプロパティを介したNSArrayです。値は配列に組み込まれ、元に戻されます。リソースカウントはこのように機能しませんか?つまり、CGLayerRefの代わりに、上記のコードを使用してNSObjectをNSArrayに格納している場合、リソースのカウントは機能しますか?そうでない場合は、中間のled_infoプロパティを削除し、ループ内から直接配列にアクセスしますか?

4

1 に答える 1

0
于 2012-11-27T07:19:22.590 に答える