2012-04-25 7 views
18

ARCを使用してコンパイルする場合、メソッド引数はメソッドの先頭に保持され、最後に解放されることがよくあります。この保持/解放のペアは不要であると思われ、ARCが "とにかく書いたコードを生成する"という考え方と矛盾しています。暗闇の中の誰も、ARC前の日は、ただ安全な側にあるために、すべての方法論論に余分な保持/解放を行ったのではないでしょうか?ARCはメソッド引数を保持するのはなぜですか?

は考えてみましょう:

@interface Test : NSObject 
@end 

@implementation Test 

- (void)testARC:(NSString *)s 
{ 
    [s length]; // no extra retain/release here. 
} 

- (void)testARC2:(NSString *)s 
{ 
    // ARC inserts [s retain] 
    [s length]; 
    [s length]; 
    // ARC inserts [s release] 
} 

- (void)testARC3:(__unsafe_unretained NSString *)s 
{ 
    // no retain -- we used __unsafe_unretained 
    [s length]; 
    [s length]; 
    // no release -- we used __unsafe_unretained 
} 

@end 

をリリースモードでのXcode 4.3.2でコンパイルした場合には、アセンブリは(私はそれを理解することができるよように)の開始時と終了時にobjc_retainobjc_releaseへの呼び出しが含まれてい2番目の方法。どうしたの?

これは大きな問題ではありませんが、パフォーマンス重視のコードのプロファイリングにInstrumentsを使用すると、この余分な保持/解放トラフィックが表示されます。メソッドの引数を__unsafe_unretainedで飾ることで、この余分な保持/解放を避けることができます。これは3番目の例で行っていますが、そうすることはかなり嫌です。

+3

最初の例でなぜそれが起こらないのでしょうか? – Thilo

+0

分解アシスタントビューを使用して、実際に生成されたコードをいつでもチェックアウトすることができます。 – Danra

答えて

17

にObjC言語のメーリングリストからthis replyを参照してください。

コンパイラは、関数やメソッドの メモリ管理の挙動については何も知らない(これは たくさん起こる)とき、コンパイラは次のように仮定する必要があります。

1)関数またはメソッドは、アプリケーションのオブジェクトグラフ全体を完全に再配置または置換する可能性があります(おそらくそうではありませんが、 )。 2)呼び出し元が手動参照カウントコードである可能性があり、したがって というパラメータが渡された寿命は現実的にはわかりません 知ることができます。

与えられた#1および#2; ARC でなければ、オブジェクトは の時期尚早に割り当てを解除できません。この2つの前提条件により、コンパイラ はオブジェクトに渡される頻度が高くなることが強制されます。

- (void) processItems 
{ 
    [self setItems:[NSArray arrayWithObject:[NSNumber numberWithInt:0]]]; 
    [self doSomethingSillyWith:[items lastObject]]; 
} 

- (void) doSomethingSillyWith: (id) foo 
{ 
    [self setItems:nil]; 
    NSLog(@"%@", foo); // if ARC did not retain foo, you could be in trouble 
} 

もその理由かもしれないと私はARCは守備行動し、それらを保持しているように、主な問題は、あなたのメソッドの体がリリースされた引数につながる可能性があるということだと思い

あなたのメソッドにただ1つの呼び出しがあるとき、余分な保持は見えません。

+5

第2の例では、第2の例では、最初に '[s length]'を呼び出すと 's'が解放されない限り、' s'が解放されて2番目の呼び出しが存在しないオブジェクトになる可能性があります。 – JeremyP

+0

"コンパイラは、関数またはメソッドのメモリ管理動作について何も知らない"。 NSString:長さが正常に動作していることがわかるかもしれないと思う人もいます。コンパイラが十分な有益な情報を持っている場合の例はありますか?あるいは、これは本当に* all *メソッド呼び出しで起こりますか? – Thilo

+0

私はそれが価値があるのは難しいと思う。 「よく振る舞う」とは、「地域変数とは別に何もリリースしない」ということです。これはかなり強い保証です。メソッド引数の余分な保持と解放のコストはほとんどの人にとって事実上ゼロです。本当にあなたを傷つけている場合、あなたが何をしているのかを知っていれば '__unsafe_unretained'がそのケースを解決します。 – zoul

1

舞台裏では増加しません。 ARCの下でオブジェクトがStrongの場合は、それ以上の強力なポインタがなくなるまで生き残ります。しかし、これは実際にパラメータとして渡されるオブジェクトとは何の関係もありません。

2

パラメータとして渡すと、一般的に保持カウントは増加しません。しかし、NSThreadのようなものに渡している場合は、に新しいスレッドのパラメータを保持することが具体的に文書化されています。

この新しいスレッドをどのように開始しようとしているかの例はありませんが、私は決定的な答えを出すことはできません。しかし、一般的には、あなたは大丈夫です。

2

魂のさえ答えが正しいか、あるべきよりも少し深い:

渡された参照が強い変数、パラメータ変数に割り当てられているので、それは、保持されます。これだけが保持/解放の対の理由です。 (パラメータvarを__weakに設定し、何が起こるのですか?)

1つは最適化できますか?パラメータはローカル変数であるため、ローカル変数のすべての保持/解放ペアを最適化するのと同じようになります。これは、コンパイラが送信されたすべてのメッセージと関数呼び出しを含むメソッド内のホールコードを理解している場合に実行できます。これは、あまりにもそれがそれをしようとしないことはまれに適用されることができます。 (argがグループに属している人(唯一の)を指していてグループがdeallocされているとしましょう:

そして、MRCのargsを保持しないようにしました。危険なものですが、一般的に開発者はそのコードをよく知っているので、考えずに保持/解放を最適化します。

関連する問題