2011-12-16 12 views
1

UIScrollViewの中に配置したUIViewのバッキングレイヤーとしてCATiledLayerを使用しています。私のビューのinitメソッドで私は単純な線を描画するCGPathRefオブジェクトを作成しています。 drawLayer:inContext内にこのパスを描画しようとすると、スクロール/ズームしているときにEXEC_BAD_ACCESS(まれに)でクラッシュすることがあります。事前に準備したCGPathを描画するときにCATiledLayerがクラッシュする

コードは、私が唯一の標準的なCG *関数を使用しています、非常に簡単です:

- (id)initWithFrame:(CGRect)frame 
{ 
    self = [super initWithFrame:frame]; 
    if (self) { 
     CATiledLayer *tiledLayer = (CATiledLayer *)[self layer]; 
     tiledLayer.levelsOfDetail = 10; 
     tiledLayer.levelsOfDetailBias = 5; 
     tiledLayer.tileSize = CGSizeMake(512.0, 512.0); 

     CGMutablePathRef mutablePath = CGPathCreateMutable(); 
     CGPathMoveToPoint(mutablePath, nil, 0, 0); 
     CGPathAddLineToPoint(mutablePath, nil, 700, 700); 
     path = CGPathCreateCopy(mutablePath); 
     CGPathRelease(mutablePath); 
    } 
    return self; 
} 

+ (Class) layerClass { 
    return [CATiledLayer class]; 
} 

- (void) drawRect:(CGRect)rect { 
} 

- (void) drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx { 
    CGContextSetRGBFillColor(ctx, 1, 1, 1, 1); 
    CGContextFillRect(ctx, self.bounds); 

    CGContextSetLineWidth(ctx, 5); 

    CGContextAddPath(ctx, path); 
    CGContextDrawPath(ctx, kCGPathStroke); 
} 

- (void)dealloc { 
    [super dealloc]; 
} 

UPDATE: 私はこの問題は唯一のiOS 5上に存在することをnoicedている、それは4.3

+0

NSZombieEnabledを有効にすると、EXEC_BAD_ACCESSをデバッグするのが簡単です。これにより、どの割り当て解除された変数が呼び出されているかが表示されます。 – Wolfert

+0

しかし、このコードではメモリで何か悪いことはできませんが、とにかくCATiledLayerと複数のスレッドでしか起こりません。 –

+0

これはスレッドによって共有されるクラスですか、それともすべてのスレッドに独自のインスタンスがありますか? – Wolfert

答えて

5

に正常に動作します私は、カスタムMKOverlayViewにキャッシュされたCGPathオブジェクトを描画しようとするときに同様の問題に遭遇しました。

CGPathを複数のスレッドで同時に描画できないため、クラッシュが発生する可能性があります。これは、(ドキュメントで指定されているように)ポイント配列の現在のポイントへのポインタを含む不透明なクラスです。 2つ以上のスレッドが描画中にこの配列を同時に反復すると、未定義の動作とクラッシュが発生する可能性があります。

私は(不完全なコピーを防止するためにmutexロック内に含まれる)各描画スレッドにCGPathにオブジェクトをコピーすることによってこの問題を回避働い

:あなたは上のコピーを行うためのメモリオーバーヘッドを懸念している場合は

//lock the object's cached data 
pthread_mutex_lock(&cachedPathMutex); 
//get a handle on the previously-generated CGPath (myObject exists on the main thread) 
CGPathRef myPath = CGPathCreateCopy(myObject.cachedPath); 
//unlock the mutex once the copy finishes 
pthread_mutex_unlock(&cachedPathMutex); 

// all drawing code here 
CGContextAddPath(context, myPath); 
... 
... 
CGPathRelease(myPath); 

各スレッド、あなたはまた、キャッシュされたCGPathにオブジェクトを直接操作することができますが、ミューテックスは(種類のねじ描画の目的に反し)全体の描画処理中にロックされたままにする必要があります:

//lock the object's cached data 
pthread_mutex_lock(&cachedPathMutex); 

//get a handle on the previously-generated CGPath (myObject exists on the main thread) 
CGPathRef myPath = myObject.cachedPath; 

// draw the path in the current context 
CGContextAddPath(context, myPath); 
... 
... 

//and unlock the mutex 
pthread_mutex_unlock(&cachedPathMutex); 

私は資格ます私の答えはそれを言って私はQuartzのマルチスレッド描画の専門家ではなく、私のシナリオでこのアプローチがクラッシュを解決しただけです。がんばろう!

UPDATE: iOS 5.1.0がリリースされていて、問題の根本的な原因が実際にはiOS 5.0.xのQuartzでバグだったように見えてきました。 CGOSCreateCopy()とmutex呼び出しを削除してiOS 5.1.0をテストすると、iOS 5.0.xで発生したクラッシュは見られません。

//get a handle on the previously-generated CGPath (myObject exists on the main thread) 
CGPathRef myPath = myObject.cachedPath; 

// all drawing code here 
CGContextAddPath(context, myPath); 
... 
... 
//drawing finished 

チャンスは我々がしばらくのiOS 5.0.xのを支援することがありますされているので、それは(わずかなパフォーマンスヒットを除く)あなたのコードでミューテックスを保つために傷つけ、または単にバージョンチェックを実行しません描画する前に。

+0

あなたの答えをありがとう、それは多く説明します! –

+0

ありがとう - 私のカスタムMKOverlayViewで同様の問題が修正されました。 もし私がConcurrency Programming Guideを読んでいるのが正しければ、ミューテックスを使うのではなく、シリアルキューに対してGCDスタイルのdispatch_synchを使うほうが少し効率的かもしれません。 パスのコピーを作成するのは面倒です。シリアル・キューまたはロック内にコピーを作成する方が毎回パスを最初から作成するよりも速いかどうかという疑問があります。答えは、パス作成コードの複雑さ/効率に左右されます。 – StephenT

関連する問題