1

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

コードは非常に単純です。標準の 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];
}

更新: この問題は iOS 5 にのみ存在することに気づきました。4.3 では問題なく動作します。

4

1 に答える 1

5

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

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

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 を使用したマルチスレッド描画の専門家ではなく、このアプローチが私のシナリオでのクラッシュを解決したということだけを言って、私の答えを限定します。幸運を!

更新: iOS 5.1.0 がリリースされたので、このコードを再確認しました。問題の根本的な原因は、実際には iOS 5.0.x の Quartz のバグだったようです。CGPathCreateCopy() とミューテックス呼び出しを削除して 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 をサポートする可能性があるため、コード内にミューテックスを保持したり (わずかなパフォーマンス ヒットを除いて)、描画する前にバージョン チェックを実行したりしても問題はありません。

于 2012-01-15T19:54:19.083 に答える