注:これは、特に非ARCメモリー管理に適用されます。
これは非常に多くの見解を持っており、チェックされた回答は「CodeはObjective-Cでのメモリ管理の仕組みに関する知識の深刻な欠如を示しています」と適切に述べているので、特定のエラーを指摘している人はいないので、それらに触れた答え。
メソッドの呼び出しについて覚えておく必要のあるベースラインレベルのルール:
メソッド呼び出しにalloc、new、copy、retainという単語が含まれている場合、作成されたオブジェクトの所有権があります。¹オブジェクトの所有権がある場合、それを解放するのは私たちの責任です。
メソッド呼び出しにこれらの単語が含まれていない場合、作成されたオブジェクトの所有権はありません。¹オブジェクトの所有権がない場合、オブジェクトを解放することは私たちの責任ではないため、絶対に行わないでください。
OPのコードの各行を見てみましょう。
-(UIImage *) downloadImageToFile {
新しい方法を始めました。そうすることで、作成された各オブジェクトが存在する新しいコンテキストを開始しました。これを少し覚えておいてください。次の行:
NSURL * url = [[NSURL alloc] initWithString: self.urlField.text];
私たちが所有している:そこにあるallocurl
という単語は、私たちがオブジェクトの所有権を持っており、自分でそれを解放する必要があることを示しています。そうしないと、コードがメモリリークを起こします。
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
私たちは所有していませんpaths
:4つの魔法の言葉を使用していないので、私たちは所有権を持っておらず、自分でそれを解放してはなりません。
NSString *documentsDirectory = [paths objectAtIndex:0];
私たちは所有していませんdocumentsDirectory
:魔法の言葉はありません=所有権はありません。
[paths release]
数行前に戻ると、パスを所有していないことがわかります。したがって、このリリースでは、存在しなくなったものにアクセスしようとすると、EXC_BAD_ACCESSがクラッシュします。
NSString * path = [documentsDirectory stringByAppendingString:@"/testimg.png"];
私たちは所有していませんpath
:魔法の言葉はありません=所有権はありません。
NSData * data = [[NSData alloc] initWithContentsOfURL:url];
私たちが所有している:そこにあるallocdata
という単語は、私たちがオブジェクトの所有権を持っており、自分でそれを解放する必要があることを示しています。そうしないと、コードがメモリリークを起こします。
次の2行は、何も作成または解放しません。次に、最後の行があります。
}
メソッドが終了したため、変数のコンテキストは終了しました。コードを見ると、との両方を所有していることがわかりますが、url
どちらdata
もリリースしていません。その結果、このメソッドが呼び出されるたびに、コードがメモリリークを起こします。
NSURL
オブジェクトurl
はそれほど大きくないので、リークに気付かない可能性がありますが、それでもクリーンアップする必要がありますが、リークする理由はありません。
NSData
オブジェクトはpng画像であり、data
非常に大きくなる可能性があります。このメソッドが呼び出されるたびに、オブジェクトのサイズ全体がリークされます。テーブルセルが描画されるたびにこれが呼び出されたと想像してください。アプリ全体がクラッシュするのにそれほど時間はかかりません。
では、問題を解決するために何をする必要がありますか?非常に簡単です。オブジェクトが不要になったらすぐに解放する必要があります。通常は、最後に使用した直後にオブジェクトを解放する必要があります。
-(UIImage *) downloadImageToFile {
// We own this object due to the alloc
NSURL * url = [[NSURL alloc] initWithString: self.urlField.text];
// We don't own this object
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
// We don't own this object
NSString *documentsDirectory = [paths objectAtIndex:0];
//[paths release] -- commented out, we don't own paths so can't release it
// We don't own this object
NSString * path = [documentsDirectory stringByAppendingString:@"/testimg.png"];
// We own this object due to the alloc
NSData * data = [[NSData alloc] initWithContentsOfURL:url];
[url release]; //We're done with the url object so we can release it
[data writeToFile:path atomically:YES];
[data release]; //We're done with the data object so we can release it
return [[UIImage alloc] initWithContentsOfFile:path];
//We've released everything we owned so it's safe to leave the context
}
メソッドの最後でコンテキストが閉じる直前に、すべてを一度にリリースすることを好む人もいます。その場合、両方[url release];
とが閉じ中括弧[data release];
の直前に表示されます。}
できるだけ早くそれらをリリースすると、コードがより明確になり、後でオブジェクトを処理した場所を正確に確認するときに明確になります。
alloc
要約すると、メソッド呼び出しでnew
、、、copy
またはで作成されたオブジェクトを所有retain
しているため、コンテキストが終了する前にオブジェクトを解放する必要があります。私たちは他に何も所有しておらず、決して解放してはなりません。
¹4つの単語には実際に魔法のようなものは何もありません。これらは、問題のメソッドを作成したAppleの人々によって一貫して使用されているリマインダーにすぎません。独自のクラスに対して独自の初期化またはコピーメソッドを作成する場合、適切なメソッドにalloc、new、copy、またはretainという単語を含めるのは私たちの責任であり、名前にそれらを使用しない場合は、所有権が渡されたかどうかを自分で覚えておく必要があります。