0

私は iOS と Objective-C の両方の開発に非常に慣れていないため、iOS 6 から iOS 7 へのアプリケーションの移植を任されています。最終的にクラッシュします。iOS7 デバイスでは、約 75% の確率でアプリが正常に起動し、約 25% の確率でハングします。iOS6 デバイスでこの動作に遭遇したことはありません。

デバッガーを使用して、「ImageLoader」の問題と思われるものに絞り込みました。これがApple APIまたは外部ライブラリの一部であるかどうかを判断するのに苦労しています-Appleのドキュメントを見つけることができませんが、どこにもライブラリとして見つけることができず、ImageLoaderの使用Apple API を使用した直接の結果です。[[NSFileManager defaultManager] createFileAtPath:_filePath contents:NULL attributes:NULL];

ロックが発生すると、ImageLoader にアクセスする 2 つのスレッドが存在します。どちらも ImageLoader::recursiveInitialization() から少し下がったところにあります。1 つは ImageLoader::recursiveSpinLock() (具体的には 'dyld`OSAtomicCompareAndSwapLongBarrier') でスタックし、もう 1 つは 'libsystem_kernel.dylib`__psynch_mutexwait:' でスタックします。私にとって最も可能性が高いと思われるのは、これが不適切に実装されたミューテックスであり、おそらくカーネルにあるということです。

スレッドの 1 つはメイン アプリケーション スレッドで、そのコードを簡単に確認できます。もう一方のスレッドは、常にアプリケーションによって作成される 2 番目のスレッドであり、アプリケーション コードは含まれません。どこで、どのように作成されているかを把握する方法がわかりません。

関連コード:

[[NSFileManager defaultManager] removeItemAtPath:[_filePath stringByAppendingString:tmp] error:nil];
if (![[NSFileManager defaultManager] moveItemAtPath:_filePath toPath:[_filePath stringByAppendingString:tmp] error:nil]) {
    return;
}

NSFileHandle *read = [NSFileHandle fileHandleForReadingAtPath:[_filePath stringByAppendingString:tmp]];
[[NSFileManager defaultManager] createFileAtPath:_filePath contents:NULL attributes:NULL];

つまり、このコードは _filePath + ".tmp" のファイルを削除し、_filePath のファイルを _filePath + ".tmp" に移動し、_filePath + ".tmp" で読み取りハンドルを取得し、最後に _filePath に新しいファイルを作成します。このコード ブロックの最後の行でロックされ、それ以上進行しません。かなり平凡なコードのように見えるので、経験不足のために見逃している OS の癖があるのではないかと考えています。

私はかなりの検索を行いましたが、同様の問題を抱えている人は他に見つかりませんでした。なぜこれが起こっているのか、またはこれをさらにトラブルシューティングするために実行できるいくつかの手順を知っている人はいますか?

編集:このコードは、ImageLoader を同時に使用する可能性のある 2 つのスレッドをディスパッチすることを発見しました。そのコードを変更して同じスレッドで (つまり、同期的に) 操作することで、このエラーは回避されます。~~

これはまだ ImageLoader コード内のバグのように見えます。スタック トレースはロック/ミューテックス メカニズムを示していますが、明らかに十分にスレッド セーフではありません。

編集2:気にしないでください。頻度を減らすことしかできませんでした-これはまだ発生していますが、スレッドディスパッチコードを削除した後、約10%の時間で「のみ」発生します。

4

1 に答える 1

1

さまざまなスレッドでさまざまな NSFileManager オブジェクトを使用してみます。[NSFileManager defaultManager] を使用する代わりに、[[NSFileManager alloc] init] で別のインスタンスを作成します。それが役立つかどうかを確認してください。ドキュメントには、デリゲートを使用していない限り、異なるスレッドで同じインスタンスを使用できるはずであると書かれています。ただし、別のインスタンスを使用してみると、コードでエラーが発生しているかどうかを確認するのに役立ちます。

于 2013-11-02T07:51:18.320 に答える