nil / NULLブロックを実行すると、なぜバスエラーが発生するのですか?
質問
私はブロックをよく使うようになり、すぐにnilブロックがバスエラーを引き起こすことに気づきました。
typedef void (^SimpleBlock)(void);
SimpleBlock aBlock = nil;
aBlock(); // bus error
これは、nilオブジェクトへのメッセージを無視するObjective-Cの通常の動作に反しているように思えます。
NSArray *foo = nil;
NSLog(@"%i", [foo count]); // runs fine
したがって、私はブロックを使う前に通常のnilチェックに頼らざるを得ません。
if (aBlock != nil)
aBlock();
またはダミーブロックを使用する。
aBlock = ^{};
aBlock(); // runs fine
他の選択肢はないのでしょうか?nilブロックは単にnopではだめな理由があるのでしょうか?
どのように解決するのですか?
もう少し、完全な回答で説明したいと思います。まず、このコードを考えてみましょう。
#import <Foundation/Foundation.h>
int main(int argc, char *argv[]) {
void (^block)() = nil;
block();
}
これを実行すると
block()
の行でクラッシュします (32 ビット アーキテクチャで実行した場合。これは重要です)。
EXC_BAD_ACCESS (コード=2, アドレス=0xc)
では、なぜそうなるのでしょうか?さて、その
0xc
が最も重要なビットです。このクラッシュは、プロセッサがメモリアドレス
0xc
. これは、ほぼ間違いなく、まったく間違った行為です。そこに何かがあるとは思えません。しかし、なぜこのメモリロケーションを読もうとしたのでしょうか?それは、ブロックが実際にボンネットの下で構築される方法によるものです。
ブロックが定義されるとき、コンパイラは実際にこの形式のスタック上の構造体を作成します。
struct Block_layout {
void *isa;
int flags;
int reserved;
void (*invoke)(void *, ...);
struct Block_descriptor *descriptor;
/* Imported variables. */
};
そして、ブロックはこの構造体へのポインタとなります。4番目のメンバーである
invoke
この構造体の4番目のメンバーであるinvoke
メンバの前にある構造体のバイト数を数えると、10進数で12、16進数でCであることがわかります。
つまり、ブロックが呼び出されると、プロセッサはブロックのアドレスを取り、12を加えて、そのメモリアドレスに保持されている値をロードしようとするのです。そして、そのアドレスにジャンプしようとします。しかし、ブロックがnilであれば、アドレス
0xc
. これは明らかに不正なアドレスなので、セグメンテーションフォールトが発生します。
Objective-C のメッセージコールのように黙って失敗するのではなく、このようにクラッシュしなければならない理由は、本当に設計上の選択です。コンパイラーは、ブロックをどのように呼び出すかを決定する作業を行っているため、ブロックが呼び出されるたびに nil チェック コードを注入する必要があります。これでは、コードサイズが大きくなり、パフォーマンスも悪くなります。もう一つの方法は、nilチェックを行うトランポリンを使用することです。しかし、これもパフォーマンスが低下します。Objective-Cのメッセージは、実際に呼び出されるメソッドを調べる必要があるため、すでにトランポリンを経由しています。ランタイムはメソッドの遅延注入やメソッド実装の変更を許可しているので、どのみちすでにトランポリンを通過しているのです。この場合、nilチェックを行うことによる余分なペナルティは重要ではありません。
根拠を説明するのに少しは役に立ったでしょうか。
関連
-
[解決済み】APIを実装する際に、ブロック内でselfをキャプチャしないようにするにはどうすればいいですか?
-
[解決済み] Objective-CのNULLとnilの比較
-
[解決済み] Objective-CでnilとNULLはいつ使うべきですか?
-
[解決済み] performSelectorの使用: メソッドを呼び出すだけと比較した場合
-
[解決済み] 警告 "format が文字列リテラルでなく、format の引数がない場合"
-
[解決済み] Objective-Cでオブジェクトのプロパティリストを取得する
-
[解決済み] NSNumberのインクリメント方法
-
[解決済み] CocoaでperformSelector:withObject:afterDelay:をプリミティブ型で使用する方法は?
-
[解決済み] Xcodeバージョン(CFBundleShortVersionString)とビルド(CFBundleVersion)の違いについて
-
[解決済み] writeToFile:atomically:はデータを上書きするのか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] Objective-Cのセレクタ?
-
[解決済み] ARCで__autoreleasing ownership修飾子を記述する必要があるのは、どのような場合ですか?
-
[解決済み] Objective-Cでオブジェクトをコピーする方法
-
[解決済み] Objective-CでSwift構造体を使用する方法
-
[解決済み] Objective-Cで`oneway void`の使用例?
-
[解決済み] APNSのデバイストークンは、一度作成すると変更されることはありますか?
-
[解決済み] NSStringをstd::stringに変換するにはどうしたらいいですか?
-
[解決済み] Objective-CのReadonlyプロパティ?
-
[解決済み] CocoaでperformSelector:withObject:afterDelay:をプリミティブ型で使用する方法は?
-
[解決済み] iOSです。2つの日付を比較する