2016-11-04 16 views
0

当社のチームは既存のアプリケーションで動作します。そのプロジェクトは非ARC(自動参照カウント)です。コードに続くオブジェクトを解放することに疑念が生じます。非ARCプロジェクト:NSMutableArray、NSStringメモリリーク

コード1:このコードを実行するとクラッシュしないのはなぜですか?

NSMutableArray *arraytest=[[NSMutableArray alloc]init]; 
for(int i=0;i<100;i++) 
{ 
    NSString *str=[NSString stringWithFormat:@"string:%d",i]; 
    [arraytest addObject:str]; 
} 
NSLog(@"arraytest before:%@",arraytest); 
[arraytest release]; 
NSLog(@"arraytest after:%@",arraytest); 

同様のコード:可変コピーと

コード2:変更した後、次のコードは、最後の行でクラッシュします。

NSMutableArray *arraytest=[[NSMutableArray alloc]init]; 
for(int i=0;i<100;i++) 
{ 
    NSString *str=[NSString stringWithFormat:@"string:%d",i]; 
    [arraytest addObject:str]; 
} 
NSLog(@"arraytest before:%@",arraytest); 
NSMutableArray *copyarray=[arraytest mutableCopy]; 
[arraytest release]; 
NSLog(@"copyarray:%@",copyarray); 
NSLog(@"arraytest after:%@",arraytest); 

なぜこの行にメモリリークがありますか?

enter image description here

そして、なぜこの行のメモリリークはありますか?

enter image description here

メモリリークせずに上記のコードを実行するための正しい方法は何ですか?私たちの会社の人たちは、autoreleaseがコードの上で使われるべきではないと伝えます。

答えて

0

はあなたの最初の例を考えてみます(配列の+1から0へのカウントを保持を減らす)[arraytest release]と呼ばれた後、オブジェクトがarraytestで指されるので、最後NSLog文は、非常に危険である

NSMutableArray *arraytest = [[NSMutableArray alloc] init]; 

// `arraytest` populated ... 

NSLog(@"arraytest before:%@", arraytest); 
[arraytest release]; 
NSLog(@"arraytest after:%@", arraytest); // DANGER: referencing dangling pointer!!! 

はされているでしょう割り当てが解除され、arraytestは、その解放されたメモリに対してdangling pointerになりました。割り当てが解除された後は、ポインタを決して参照しないでください。時にはあなたのように見えるかもしれませんが、安全ではないので、あなたのアプリは突然クラッシュすることがあります。 (あなたはゾンビを使用した場合は、しかし、それは安全に誤っこのダングリングポインタを使用するように試みのことを警告しているだろう。)


あなたの第二の例を検討します。この例では

NSMutableArray *arraytest = [[NSMutableArray alloc] init]; 

// `arraytest` populated ... 

NSLog(@"arraytest before:%@",arraytest); 
NSMutableArray *copyarray = [arraytest mutableCopy]; 
[arraytest release]; 
NSLog(@"copyarray:%@", copyarray); 
NSLog(@"arraytest after:%@", arraytest); // DANGER: referencing dangling pointer!!! 

を、まだあなたは、あなたがそれをリリースした後、非常に危険なNSLogarraytestを最後に持っていて、そのぶら下がっているポインタを使用すると簡単にクラッシュする可能性があります。だからあなたはそれを取り除きたいと思うでしょう。

これで、リークが発生しました。 arraytestが最初に指したオブジェクトをリリースしたときに、copyarrayが指し示すオブジェクトを解放していない場合は、mutableCopyの結果を返します。したがって、この新しいcopyarrayインスタンスにはリークが発生します。したがって、最初にarraytestを作成したときに割り当てられた文字列は、リークされたcopyarrayによって参照され、リークされます。

このルーチンの最後に[copyarray release]を追加した場合、リークしている配列と漏れている文字列の両方が解決されています。


今、唯一の最終インスツルメンツ画面のスナップショットに示されているあなたの第三の例、考えてみます。この最後の例で

NSMutableArray *arraytest = [[NSMutableArray alloc] init]; 
for (int i=0; i<100; i++) { 
    NSString *str = [NSString stringWithFormat:@"string:%d", i]; 
    [arraytest addObject:str]; 
    [str release];      // DANGER: released `str` whose ownership was never transferred to you!!! 
} 
NSLog(@"arraytest before:%@",arraytest); 
NSMutableArray *copyarray=[arraytest mutableCopy]; 
[arraytest release]; 
NSLog(@"copyarray:%@",copyarray); 
NSLog(@"arraytest after:%@",arraytest); // DANGER: referencing dangling pointer!!! 

を、私たちはあなただけに追加された文字列をoverreleasingことで、問題を配合しています配列。だから、(自動解放プールが排出されたときに効果的に保持カウントがゼロになります)自動解放オブジェクトを作成し、(+1へのカウントを保持してその効果を上げる)の配列に追加して、strをリリース(それはバック時に+0に、カウントを保持しています削減プールの排水)。

現在、アレイは、自動解放プールが空になったときにリリースされる可能性があるオブジェクトを参照しているため、不安定な状況にあり、手間のかかるポインタの配列で終わってしまいます。さらに悪いことに、この配列が適切に解放された場合、それらの文字列のすべてが上書きされます。上記、第二の例で説明したように

とは、もちろん、あなたはまだ、配列の漏れを持っています。しかし、もしあなたがこの copyarrayを適切にリリースしたならば、それらの文字列のすべてが過剰にリリースされます。

おそらく

この時点では言うまでもないが、このリークを解消する方法は、単にcopyarrayを解放することです:

NSMutableArray *arraytest = [[NSMutableArray alloc] init]; 
for (int i=0; i<100; i++) { 
    NSString *str = [NSString stringWithFormat:@"string:%d", i]; 
    [arraytest addObject:str]; 
} 
NSLog(@"arraytest before:%@", arraytest); 
NSMutableArray *copyarray = [arraytest mutableCopy]; 
[arraytest release]; 
NSLog(@"copyarray:%@", copyarray); 
[copyarray release]; 

これは、あなたがreleaseを呼び出すための責任がある、すなわちこと、Basic Memory Management Rulesを次のあなたがのおかげで所有しているオブジェクトはallocnewcopy、またはmutableCopy)で始まるメソッドからそれらを受け取りました。


閉じ観測のカップル:

  1. 手動参照カウントを使用するつもりなら、私はあなたがXcodeのの静的アナライザを頻繁に利用することをお勧めしたいが(シフト + コマンド + B、またはXcodeの「Product」メニューの「Analyze」)。手動の参照カウントメモリの問題を特定することは驚くべきことです。インストゥルメントは便利ですが、上記の3番目の例で示したように、間違った結論に簡単に引き寄せることができます(例:「そう、すべての文字列を解放する必要があります)。スタティックアナライザは、これらの問題のいくつかをあなたに指摘していたでしょう。

    ボトムラインは、常にあなたがさらに進む前に、静的アナライザからの健康のクリーン法案を持っていることを確認してください。インストゥルメントが問題の内容を正確に伝えることができた場合、その問題をリバースエンジニアリングすることは何の意味もありません。

  2. 私は特定のダングリングポインタは、あなたのアプリがクラッシュしなかったが、他がやったという事実から何らかの結論を描くない助言します。予期しないことだ。ゾンビを有効にした場合(一時的には、実稼働アプリケーションではなく、開発/テスト目的でのみ)、以前に割り当て解除されたオブジェクトを参照しようとする試みには注意が必要です。

  3. 私はあなたのテストでNSStringを使用していることに気付きます。NSStringには、非標準的な動作を引き起こす可能性のある内部メモリの最適化があることに注意してください。これらの種類の実験では、NSStringを使用することには注意が必要です。

    間違ってはいけません:Basic Memory Management Rulesのすべてに従うと、NSStringは適切に動作します。しかし、意図的にメモリ管理ルールに従わなかったときにどのようなエラーやクラッシュが発生するのか調べる場合は、NSStringが誤解を招く可能性があることに注意してください。

  4. 言うまでもなく、ARCを使用すると、あなたの人生は大幅に簡素化されます。詳細は、Transitioning to ARC Release Notesを参照してください。

関連する問題