16

Apple のマルチスレッド ドキュメントNSIndexPathには、スレッドセーフかどうかが記載されていません。不変クラスとして、私は一般的にそれがスレッドセーフであることを期待しています。

NSIndexPath以前は、インスタンスが共有され、グローバルに一意であると述べていたドキュメントが使用されていたと確信しています。しかし、それは今では消えているようで、iOS5 / Mac OS X 10.7 でデザインが変更されたのではないかと疑っています。

Mac OS X 10.6 (Snow Leopard) を使用しているお客様から、インデックス パスにアクセスしようとしてクラッシュしているように見えるクラッシュ レポートが多数寄せられています。したがって、私は疑問に思います: 実際のインスタンスはスレッドセーフですが、共有キャッシュからそれらを引き出すためのロジックはそうではありませんか? 誰か洞察力がありますか?

ところで、スタック トレースの例を次に示します。

Dispatch queue: com.apple.root.default-priority
0 libobjc.A.dylib 0x96513f29 _cache_getImp + 9
1 libobjc.A.dylib 0x965158f0 class_respondsToSelector + 59
2 com.apple.CoreFoundation 0x948bcb49 ___forwarding___ + 761
3 com.apple.CoreFoundation 0x948bc7d2 _CF_forwarding_prep_0 + 50
4 com.apple.Foundation 0x994b10c5 -[NSIndexPath compare:] + 93
5 com.apple.Foundation 0x99415686 _NSCompareObject + 76
6 com.apple.CoreFoundation 0x948af61c __CFSimpleMergeSort + 236
7 com.apple.CoreFoundation 0x948af576 __CFSimpleMergeSort + 70
8 com.apple.CoreFoundation 0x948af38c CFSortIndexes + 252
9 com.apple.CoreFoundation 0x948fe80d CFMergeSortArray + 125
10 com.apple.Foundation 0x994153d3 _sortedObjectsUsingDescriptors + 639
11 com.apple.Foundation 0x994150d8 -[NSArray(NSKeyValueSorting) sortedArrayUsingDescriptors:] + 566

私にとって、それは、NSIndexPath自分自身を割り当て解除されたインスタンスと比較しようとしているインスタンスです。

4

2 に答える 2

4

これまでのところ、私が持っている最良の答えは次のとおりです。

OS X 10.7 および iOS 5 以降、NSIndexPathスレッドセーフです。それ以前は、インスタンスは不変であるためスレッド セーフですが、既存のインスタンスの共有取得はそうではありません。

オンデマンドでインデックス パスを返すメソッドに対して、次のようにしました。

- (NSIndexPath *)indexPath;
{
    NSIndexPath *result = … // create the path as appropriate

    return [[result retain] autorelease];
}

コードの最後の行を実装して以来、インデックス パスからのクラッシュ レポートはなくなりました。

インデックス パスは、-indexPathByAddingIndex:またはによって作成され+indexPathWithIndex:ます。

私が見ている結果は、(10.7/iOS5 より前の) これらのメソッドが既存のNSIndexPathインスタンスを返していることを確信させてくれます。ただし、そのインスタンスは現在のスレッドによって保持されていないため、最初にインスタンスを作成したスレッド (この場合はメイン) がパスを解放し (おそらく自動解放プールをポップすることによって)、ワーカー スレッドにダングリング ポインターを残します。質問に見られるように、使用するとクラッシュします。

私の分析が正しければ、私が追加したretain/autoreleaseダンスは、ある競合状態を別の可能性の低いものに単に置き換えているだけなので、それは少し恐ろしいことです.

10.7/iOS5 より前では、1 つの真の回避策しか考えられませんでした。それは、インデックス パスのすべての作成をメイン スレッドに制限することです。そのようなコードが頻繁に呼び出されるとかなり遅くなる可能性があるため、バックグラウンド スレッドが使用する独自のインスタンス キャッシュを維持することで、メモリを犠牲にして改善することができます。キャッシュがパスを保持している場合、メイン スレッドによって割り当てが解除されないことがわかります。

于 2012-02-25T12:10:09.027 に答える
1

Apple は NSIndexPath をスレッドセーフとして具体的にリストしていませんが、不変クラスは一般に安全であり、可変クラスは一般に安全ではないと言っています。NSIndexPath は不変であるため、スレッドセーフであると想定しても安全です。

ただし、「スレッド セーフ」とは、別のスレッドで使用する前に、あるスレッドによって解放されてもクラッシュしないという意味ではありません。スレッド セーフとは、一般に、ミューテーター メソッドにロックが含まれており、2 つのスレッドがプロパティを同時に設定することによる不具合を防止することを意味します (ミューテーター メソッドのないクラスは一般的にスレッド セーフですが、遅延ゲッターや共有インスタンスも問題を引き起こす可能性があるのはそのためです)。

あなたのバグは、自動解放プールまたはオブジェクトが制御外の時点で解放される他のメカニズムを使用している可能性が高いようです。同時にアクセスされるオブジェクトは、存続期間を制御できるように、存続期間の長いクラスのプロパティに格納する必要があります。

自動解放されたオブジェクトを作成し、そのオブジェクトへの強い参照をすべて削除した後で別のスレッドからアクセスすることは、問題のオブジェクトが「スレッドセーフ」であるかどうかに関係なく、追跡が困難なクラッシュを引き起こす可能性が高い危険なレーシング ゲームです。

于 2012-02-21T08:20:07.297 に答える