7

私は初めてiOS/Objective-Cで、メモリの解放を正しく理解していません。それをテストするために、空のARC有効な iPhone プロジェクトを作成し、非常に単純なテスト クラスを作成しました。

#import "MemTest.h"

@implementation MemTest {

}

-(void) start {
    for (int i = 0; i < 1500000; i++) {
        NSMutableString *myString = [NSMutableString string];

        // The appended string is 2000 characters long in the real test class.
        [myString appendString:@"12345678901234567890123456 <very long>"];

        if (i % 1000 == 0) {
            NSLog(@"i = %d", i);
        }

        myString = nil;
    }
}

@end

次の場所でテストを開始するだけですAppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    MemTest *test = [[MemTest alloc] init];
    [test start];

    ....
}

アプリケーションは (予想どおり) 多くの適切な数値 "i = xy" を出力しますが、反復ごとにメモリ使用量が増加し、最終的にアプリケーションがクラッシュします。

....
2012-12-06 20:17:40.193 MemTestApp[19250:11303] i = 930000
2012-12-06 20:17:40.208 MemTestApp[19250:11303] i = 931000
MemTestApp(19250,0xac63f2c0) malloc: *** mmap(size=16777216) failed (error code=12)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug

だから私の質問は:メモリ使用量がまったく増加しているのはなぜですか?

を使用する場合、nilを割り当てることでメモリを解放する必要があると思いましたARC。ここで何が欠けていますか?

4

4 に答える 4

7

いくつかのことがうまくいかない可能性があります。

  1. 実際には ARC が有効になっていない可能性があります。それを再確認する必要があります。最も簡単な方法は-retain、コードに a をスローして、コンパイラ エラーがスローされるようにすることです。

  2. ARC は、オブジェクトが自動解放プールに入るのを必ずしも妨げません。可能であればそれをキャッチしようとしますが、保証はしません。特に、-O0(最適化なし) では、オブジェクトが自動解放プールに入るのを妨げないことがよくあります。これはおそらくあなたに起こっていることです。

  3. より高い最適化レベルでも、ARC 対応コードが自動解放をキャッチする保証はありません。

@autoreleasepool{}ループの内側に貼り付けるとfor、メモリ使用量がなくなるはずです。または、 を使用する代わりに、 autorelease プールをまったく使用しない を[NSMutableString string]試すこともできます* が、それ以外は ARC コードで同じように動作するはずです。[NSMutableString new]

* まあ、NSMutableString必要に応じて自動解放プールを内部で自由に使用できます

于 2012-12-06T19:28:50.517 に答える
4

だから私の質問は:メモリ使用量がまったく増加しているのはなぜですか?

単一のループ内ですべての割り当てを行っているためです。これらの文字列はすべて自動解放されたオブジェクトであり、実行ループのたびに発生する、一番上の自動解放プールが空になるとすぐにクリーンアップされます。ただし、実行ループに実行の機会をまったく与えていないため、自動解放プールが空になることはなく、メモリが不足します。

ARC を使用すると、個々のオブジェクトを管理する心配から解放されますが、ARC は同じ規則に従って動作するため、Objective-C でのメモリ管理がどのように機能するかを理解する必要があります。

于 2012-12-06T19:29:09.503 に答える
1

この[NSMutableString string]メソッドは、「自動解放された」オブジェクトを返します。これは、オブジェクトが「自動解放」プールに入れられることを意味します。プールが空になると、オブジェクトは解放されます (そして、オブジェクトへの強い参照がなくなると、割り当てが解除されます)。autorelease プールは、実行ループの最後 (システムが別のイベントを待ってスリープ状態になる直前) に自動的に排出されます。

自動解放されたオブジェクトを大量に割り当てる可能性のあるループを作成する場合、独自の自動解放プールを管理したい場合があります。

-(void) start {
    for (int i = 0; i < 1500000; i++) {
        @autoreleasepool {
            NSMutableString *myString = [NSMutableString string];
            ...
        }
    }
}

このコードは、各ループ反復の開始時に新しい自動解放プールを作成し、各ループ反復の終了時にそれを排出します。したがって、作成した各文字列はループの最後に解放され、コード例では、他に何も保持されないため割り当てが解除されます。

詳細については、『Advanced Memory Management Programming Guide』、特に「Using Autorelease Pool Blocks」の章をお読みください。

于 2012-12-06T19:31:22.903 に答える
1

プロジェクトに ARC があるからといって、自動解放されたオブジェクトが別の方法で解放されるとは思いません。実際、自動解放プールはまったく同じように機能します。ループ内で多くのオブジェクトを割り当てているため、反復中にプールが空になることはありません。

autoreleasepoolこれで問題が解決するかどうかを確認するには、ループ本体内でnew を直接強制してみてください。もちろん、これはやり過ぎかもしれません。ループを 2 つのネストされたループに分割して、時々自動解放プールだけを作成することもできます。

for (int i = 0; i < TOTAL_STEPS; ++i) {
  @autoreleasepool {
    for (int j = 0; j < STEP_SIZE; ++j) {
      ..
    }
  }
}

ループ本体のスコープに対してローカルな変数であるため、ローカル変数を設定してもnil状況に違いが生じるとは思いません。コンパイラーは、他の場所では使用できないことを既に認識しています。

于 2012-12-06T19:31:27.480 に答える