10

私は Xcode 4.3.3 を使用しており、iOS 5.0 以降向けに開発しています。ARC iOS アプリケーションの開発では、ブロックを非同期操作のコールバック メカニズムとして使用し始めました。アプリはシミュレーターとデバイスで正常に動作します。

次に、初めてプロファイラーを実行しましたが、すぐにクラッシュし始めました。特に、最初のコールバック ブロックを呼び出そうとすると、EXC_BAD_ACCESS が発生しました。

少し調査した結果、動作の違いは、プロファイラーがデフォルトで「リリース モード」で実行されるためであることが明らかになりました。特に、最適化レベルが「なし [-O0]」ではなく「最速、最小 [-Os]」に設定されています。 "。

たとえば、次のコード (この質問のために簡略化されています) は、callbackBlock を実行しようとするとクラッシュします。

- (void) setCallbackBlock:(void (^)(NSString *input))block
{
    callbackBlock = block;
}

- (void) invokeCallbackWithInput:(NSString *)input
{
    if (callbackBlock) {
        callbackBlock(input);
    }
}

それにデバッグし、最適化レベルを「なし」に設定して setCallbackBlock を呼び出すと、着信ブロックは にNSStackBlockなり、 callbackBlock は になりますNSMallocBlock

ただし、最適化レベル「最速、最小」では、NSStackBlock.

setter コードを使用するように変更すると[block copy]、クラッシュの問題が修正されます (リリース ビルドでのみ iOS 5 のブロックがクラッシュすることに基づきます)。

ただし、別の関連する質問は、これは ARC では必要ないことを示しています - ブロック変数は ARC のヒープにコピーされます -なぜ Objective-C ブロックはヒープにコピーしなくても機能するのですか?

私の質問: ここで何が起こっているのか、なぜですか? (また、どうして両方の答えが正しいのでしょうか...?)

編集: callbackBlock がどのように宣言されているかを明確にするために、これらのメソッドがある @implementation のすぐ上にあるのは次のとおりです。

@interface MyClass ()
{
    void (^callbackBlock)(NSString *input);
}

@end
4

2 に答える 2

14

私の質問: ここで何が起こっているのか、なぜですか? (また、どうして両方の答えが正しいのでしょうか...?)

ARCのブロックに関する特定の質問に答えていないという点で、他の質問への答えは間違っていると私は実際に思います。問題は、ある関数/メソッドから別の関数/メソッドにスタック ベースのブロックを渡すことです。答えは、ブロック内の __block 変数をキャプチャするという、別のものに関するものです。それは別の問題です。

ご質問への回答は、ARC リリース ノートへの移行の FAQ にあります。

リターンなど、ARCモードでブロックをスタックに渡すと、ブロックは「機能します」。もう Block Copy を呼び出す必要はありません。スタックを arrayWithObjects: および保持を行うその他のメソッドに「下に」渡すときは、[^{} copy] を使用する必要があります。

したがって、これが機能する方法は、ブロック (この場合はスタックに割り当てられたブロック リテラル) を渡すと、コンパイラはその呼び出しのパラメーターを初期化するときにそのブロックをコピーしません。呼び出された関数またはメソッドには、必要に応じてそのブロック自体をコピーする責任があります。

ARC がブロックを自動的にコピーするのは、関数またはメソッドからブロックを返すときです。その場合、コンパイラは、ヒープへのコピーを行う必要があることを認識しており、そのようにします。

したがって、セッターは、ARC を使用してもブロック コピーを実行する必要があります。

それが役立つことを願っています。

于 2012-08-21T20:06:50.480 に答える
4

これは、フィローズの答えに対する長いコメントです。

ドキュメントの自動参照カウント」セクション 7.5には次のように記載されています。

__strongパラメーター変数の初期化または変数の読み取りの一部として行われる保持を除いて、__weakこれらのセマンティクスがブロックポインター型の値を保持することを要求するときはいつでも、Block_copy. オプティマイザーは、結果が呼び出しの引数としてのみ使用されていることを確認すると、そのようなコピーを削除することがあります。

そして、これは私が見た行動です。

したがって、callbackBlockが強力なインスタンス変数である場合、ARC はスタックに割り当てられたブロック パスを にコピーblockする必要があります。つまり、デバッグ バージョンは正しく、リリース バージョンはコンパイラのバグです。

これが正しければ、コンパイラのバグが見つかったので報告する必要があります。いずれにせよ、それを報告することは悪いことではありません。決定的な答えが得られるはずです。

于 2012-08-21T21:13:07.560 に答える