マーカスの答えはかなり良いですが、ここではいくつかの詳細があります(私は生成されたアセンブリを読み上げることを意味していました;実際に試して説明することが最善の方法です)。
NSObject *obj1; // declaration
obj1 = [[NSObject alloc] init];
次に、宣言を進めることはできません。
前にobj1 = [[NSObject alloc] init];, the value of
obj1 is *undefined* under Manual Retain Release, but **will be automatically set to
nil`(0)の下でARC **(これにより、Marcusが示したバグの原因が排除されます)。
[obj1 release]; // Line 2
この行はNSObjectののインスタンスにrelease
方法はobj1
により指さ呼び出します。
NSObject *obj2; // Line 3
この行は効果的に何もしません。コンパイラのオプティマイザがオンになっている場合、コードはまったく生成されません。オプティマイザがなければ、コンパイラsizeof(NSObject*)
によってスタックポインタをバンプして、スタック上にobj2
という名前の領域を確保します。
また、その行で実行する式がないため、デバッガでステップインすることはできません。あなたは限り実行が懸念しているように書いた元のコードを効果的に同一である
[[[NSObject alloc] init] release];
:注目すべき
は、あなたにコードを書き換えることができます。オプティマイザがなければ、スタックに何も格納されないという点で少し異なります。オプティマイザでは、元のコードと同じコードを生成する可能性があります。オプティマイザは、ローカル変数が不要なときにローカル変数を取り除くことができます(これは部分的に、最適化されたコードのデバッグが非常に難しい理由です)。
を考えると、この:
(11) void f()
(12) {
(13) NSObject *obj1 = [[NSObject alloc] init]; // Line 1
(14)
(15) [obj1 release]; // Line 2
(16)
(17) NSObject *obj2; // Line 3
(18)}
これは、最適化されていないx86_64のアセンブリです。 "フィックスアップ"のものは無視してください。 callq
行を見てください。上記のobjc_msgSend()の実際の呼び出しです。 x86_64では、%rdi - レジスタ - は、すべての関数呼び出しの引数0です。したがって、%rdiはメソッド呼び出しの対象が行く場所です。 %raxは戻り値に使用されるレジスタです。だから、
、あなたは「最初callq
の戻り値を取り、次のcallq
の最初の引数として渡すと言い、別のcallq続いmovq %rax, %rdi
続いcallqを、参照してください。については
あなたの変数はcallq
の後にmovq %rax, -8(%rbp)
のようなものがあります。これは "callq
によって返されたものを取り、それをスタックの現在の場所に書き出し、スタックポインタを8つの場所に移動します(スタックが小さくなる)"残念ながら、アセンブリには変数名が表示されません。
_f: ## @f
.cfi_startproc
Lfunc_begin0:
.loc 1 12 0 ## /tmp/asdfafsd/asdfafsd/main.m:12:0
## BB#0:
pushq %rbp
Ltmp2:
.cfi_def_cfa_offset 16
Ltmp3:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp4:
.cfi_def_cfa_register %rbp
subq $32, %rsp
leaq l_objc_msgSend_fixup_release(%rip), %rax
leaq l_objc_msgSend_fixup_alloc(%rip), %rcx
.loc 1 13 0 prologue_end ## /tmp/asdfafsd/asdfafsd/main.m:13:0
Ltmp5:
movq L_OBJC_CLASSLIST_REFERENCES_$_(%rip), %rdx
movq %rdx, %rdi
movq %rcx, %rsi
movq %rax, -24(%rbp) ## 8-byte Spill
callq *l_objc_msgSend_fixup_alloc(%rip)
movq L_OBJC_SELECTOR_REFERENCES_(%rip), %rsi
movq %rax, %rdi
callq _objc_msgSend
movq %rax, -8(%rbp)
.loc 1 15 0 ## /tmp/asdfafsd/asdfafsd/main.m:15:0
movq -8(%rbp), %rax
movq %rax, %rdi
movq -24(%rbp), %rsi ## 8-byte Reload
callq *l_objc_msgSend_fixup_release(%rip)
.loc 1 18 0 ## /tmp/asdfafsd/asdfafsd/main.m:18:0
addq $32, %rsp
popq %rbp
ret
Ltmp6:
Lfunc_end0:
笑いのために
、(-Os - 最速、最小の、展開されたコードのデフォルト)をオンにオプティマイザで生成されたアセンブリを見て:
最初に注意する - と、この質問(3)に戻る - それはです。最初と最後の命令の外に%rbp
の操作はありません。つまり、何もスタックにプッシュ/プルされません。文字通り、obj1
とobj2
は、コンパイラが同等のコードを生成する必要がないため、これまでに宣言されたという証拠はありません。
すべてはレジスタを介して行われ、move %rax, %rdi
が2つあることに注意してください。最初は「+alloc
の結果を取り、-init
の呼び出しの最初の引数として使用」であり、第二は、-init
の結果を取り、-release
に引数としてそれを使用する」である以外に
; %rsi
コールを機能させる第2引数はx86_64の上に存在する場所であるメソッド呼び出しについて - 。objc_msgSend()
関数への呼び出しのために - その引数が呼び出されるメソッド(セレクタ)の名前を含む常にう
Lfunc_begin0:
.loc 1 12 0 ## /tmp/asdfafsd/asdfafsd/main.m:12:0
## BB#0:
pushq %rbp
Ltmp2:
.cfi_def_cfa_offset 16
Ltmp3:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp4:
.cfi_def_cfa_register %rbp
.loc 1 13 0 prologue_end ## /tmp/asdfafsd/asdfafsd/main.m:13:0
Ltmp5:
movq L_OBJC_CLASSLIST_REFERENCES_$_(%rip), %rdi
leaq l_objc_msgSend_fixup_alloc(%rip), %rsi
callq *l_objc_msgSend_fixup_alloc(%rip)
movq L_OBJC_SELECTOR_REFERENCES_(%rip), %rsi
movq %rax, %rdi
callq *[email protected](%rip)
.loc 1 15 0 ## /tmp/asdfafsd/asdfafsd/main.m:15:0
leaq l_objc_msgSend_fixup_release(%rip), %rsi
movq l_objc_msgSend_fixup_release(%rip), %rcx
movq %rax, %rdi
popq %rbp
jmpq *%rcx # TAILCALL
Ltmp6:
Lfunc_end0:
。 さらに詳しく知りたい場合はメソッドの発送、私はbit of a guideを書いた。これはobjc_msgSend()のいくつかのバージョンですが、これまでとは異なります。
ARMコードは哲学的には同じように動作しますが、生成されたアセンブリは少し異なります。
あなたが生成されたアセンブリを見れば、私は
^^ 3行以上
これがなぜ閉鎖されたのか分かりません。 「なぜデバッガは私にこの行を乗り越えさせてくれないのですか?」というのは、多くの新しい開発者にとっては、それ自体で、やりがいのある点です。 「何かをする表現」を理解することは、「この行は純粋にあなたの[利便性のために]あり、実際にはあまり役に立たない」ということは非常に便利です。一般的に「コンパイラはこれで何を行い、どのようにランタイムとやりとりするのか」という一般的なことも非常に面白いですが、ほとんどの開発者はほとんどの場合、その詳細はほとんど必要ありません。 – bbum
なぜ私は3行目を越えることができないのか分かりません^^ – DungProton
3行目はステートメント( "これを行う")ではないので、宣言( "this exists")です。ステッピングはステートメント間を移動します。 1行目のようなイニシャライザを使った宣言は、ステッピングの目的には十分に近いです。なぜなら、イニシャライザは何かをすることです。何かを変数に入れて、1行目のイニシャライザもオブジェクトを作成します。そこに置く。 –