5

ブロック自体の内部で、Objective-C ブロックの外部で宣言されたオブジェクトのポインターを使用しようとしています。例えば:

NSError* error = nil;
[self invokeAsync:^id{
    return [self doSomething:&error];
}];

3 行目に次のようなコンパイラ エラーが表示されます。

「NSError *const__strong *」をタイプ「NSError *__autoreleasing *」のパラメーターに送信すると、ポインターの保持/解放プロパティが変更されます

何故ですか?

4

2 に答える 2

20

コンパイラ メッセージは紛らわしいですが、型の不一致があることを示しています。

しかし、そのコードは意味がないため、問題ではありません。

非同期呼び出しは、呼び出しスレッドのスタックに状態を設定できません。errorつまり、意味のある値に設定できる方法はありません。

つまり、メソッドinvokeAsync:は作業ブロックが実行される前に戻ります。invokeAsAsync:したがって、ブロックの実行の成功/失敗を示す意味のあるものを返す方法はありません。


エラーで何かを非同期的に呼び出したい場合は、コールバックが必要になります。

[self invokeAsync:^id{
    NSError *e;
    if ([self doSomething:&e])
        [self errorHappened:e];
    else
        [self asyncThingyDone];
}];
于 2013-10-31T08:21:56.373 に答える
3

ここには 2 つの問題があります。1 つ目は、@bbum によって既に指摘されているタイミングの問題です。

もう 1 つは、コンパイラがそのようなエラーを出す理由など、あなたが求めていることかもしれません。また、@bbumが言ったように、「コンパイラのメッセージは紛らわしい」。invokeSyncAndWait:最初の問題から 2 番目の問題を分離するために、呼び出しがではなく であると仮定しましょうinvokeAsync:。タイミングの問題はなくなり、2 番目の問題に集中できます。

NSError* error = nil;
[self invokeSyncAndWait:^id{
    return [self doSomething:&error];
}];

ブロックでキャプチャされたerror変数は値によるコピーであり、実際の参照ではありません。

囲んでいるレキシカル スコープにローカルなスタック (非静的) 変数は、const 変数としてキャプチャされます。それらの値は、プログラム内のブロック式のポイントで取得されます。ネストされたブロックでは、値は最も近い外側のスコープから取得されます。

ブロック内の変数への実際の参照errorがないため、そのアドレスを取得できません。これが、コンパイラがコードのコンパイルを拒否する理由です。

参照変数を取得するには、次を使用する必要があります__block

__block ストレージ修飾子で宣言された囲んでいるレキシカル スコープに対してローカルな変数は、参照によって提供されるため、変更可能です。同じ囲みレキシカルスコープ内で定義された他のブロックを含め、すべての変更は囲んでいるレキシカルスコープに反映されます。これらについては、「__block ストレージ タイプ」で詳しく説明しています。</p>

したがって、作業コードは次のとおりです。

__block NSError* error = nil;
[self invokeSyncAndWait:^id{
    return [self doSomething:&error];
}];

ただし、それでも危険なコードです。

したがって、__block 変数のアドレスは時間の経過とともに変化する可能性があります。

コンパイルの問題を説明しました。

于 2013-10-31T19:25:28.370 に答える