0

メモリ管理と割り当てをテストするために、単純なシングルビューアプリケーションを作成しviewDidAppear、で、多くのオブジェクトを含む長いループを作成するために、次のように作成しました。

- (void) viewDidAppear:(BOOL)animated
{
    NSDate * time = [NSDate date];
    [super viewDidAppear:animated];

    for (int i = 0; i < 20003; i++)
    {
        NSString * testString = [[NSString alloc] initWithString:@"This is a test string"];
        NSMutableArray * itemsArray = [[NSMutableArray alloc] init];
        for (int j = 0; j < 1000; j++)
        {
            [itemsArray addObject:testString];
        }
        if ((i % 1000) == 0)
        {
            NSLog(@"called %d", i);
        }
}
NSDate * time2 = [NSDate date];
NSTimeInterval interval = [time2 timeIntervalSinceDate:time];
[label2 setText:[NSString stringWithFormat:@"time interval: %f", interval]];

}

予想どおり、メモリリークのプロファイリング中に、メモリリークが発生し、260 MBを超える割り当てが発生しました。スクリーンショット: 画像1

しかし、このドキュメントに従うとき、私はコードを次のように変更しました:

- (void) viewDidAppear:(BOOL)animated
{
    NSDate * time = [NSDate date];
    [super viewDidAppear:animated];

    for (int i = 0; i < 20003; i++)
    {
        @autoreleasepool
        {
            NSString * testString = [NSString stringWithFormat:@"%@", @"This is a test string"];
            NSMutableArray * itemsArray = [[NSMutableArray alloc] init];
            for (int j = 0; j < 1000; j++)
            {
                [itemsArray addObject:testString];
            }
            [itemsArray release];
            itemsArray = nil;

            if ((i % 1000) == 0)
            {
                NSLog(@"called %d", i);
            }
        }
    }
    NSDate * time2 = [NSDate date];
    NSTimeInterval interval = [time2 timeIntervalSinceDate:time];
    [label2 setText:[NSString stringWithFormat:@"time interval: %f", interval]];

}

割り当て量に違いはありませんでしたが、メモリリークはなく、実行時間は2秒未満から約80秒に大幅に増加しました。 画像2

そして、リリースを使用した後も、変更はありませんでした。コード:

- (void) viewDidAppear:(BOOL)animated
{
    NSDate * time = [NSDate date];
    [super viewDidAppear:animated];

    for (int i = 0; i < 20003; i++)
    {
        NSString * testString = [[NSString alloc] initWithString:@"This is a test string"];
        NSMutableArray * itemsArray = [[NSMutableArray alloc] init];
        for (int j = 0; j < 1000; j++)
        {
            [itemsArray addObject:testString];
        }
        [testString release];
        testString = nil;
        [itemsArray release];
        itemsArray = nil;

        if ((i % 1000) == 0)
        {
            NSLog(@"called %d", i);
        }
    }
    NSDate * time2 = [NSDate date];
    NSTimeInterval interval = [time2 timeIntervalSinceDate:time];
    [label2 setText:[NSString stringWithFormat:@"time interval: %f", interval]];
}

そしてスクリーンショット: ここに画像の説明を入力してください

また、MutableArrayを明示的に(を使用して[itemsArray release])空にしても、変化は見られませんでした。

私の質問は次のとおりです。

  1. 3つのケースで割り当てられたメモリの合計が変わらないのはなぜですか(2番目と3番目のケースではメモリリークがないにもかかわらず)、大きな割り当ての場合にそれを減らす方法はありますか?障害はどこにあり、消費されるメモリを減らす方法は?

  2. 次に、適切なツールを使用して適切な数値を読み取っていますか(Profiler Instrumentsのメモリリークを使用し、すべての割り当てを参照し、割り当ての寿命において、作成されたままの状態が選択されています)、正しい方法で実行していますか?つまり、これは現在消費されているメモリの量ですか、それともアプリケーションの起動以降に消費されたメモリの合計ですか?

  3. 1番目と3番目のケースではなく、2番目のケースでループの実行時間が大幅に増加したのはなぜですか?

xCode 4.5を使用し、ARCもStoryBoardも使用せず、すべてのテストはシミュレーターで実行されました。

4

2 に答える 2

3

1.いずれの場合も同じ量のメモリを割り当てるため、合計割り当ては3つのケース間で同じになります。

2.適切なツールを使用していて、適切な数字のいくつかを読んでいますが、十分な数字を読んでいないだけだと思います。全体的なバイト数は、ご想像のとおり、基本的に現在消費しているものではありません。LiveBytesセクションを調べてください。このセクションでは、割り当て全体のほぼ半分を浪費する最初のケースと、かなりスリムな状態になってしまった2番目のケースの違いを確認します。

3.「大幅な」時間の増加は、以下の使用の違いによるものです。

[[NSString alloc] initWithString:@"This is a test string"];

@autoreleasepool  -and-
[NSString stringWithFormat:@"%@", @"This is a test string"];

はい、違いを言いました。そして、だけではありませんautorelease。だが...

最初の自動解放は、ループ内のメモリを解放するのに非効率的であることがよく知られています。それは最初から実際にはその目的ではなく、メソッドがオブジェクトをオブジェクトの呼び出し元が所有するように返すことができるように、つまり、物事を長持ちさせるように設計されているためにのみ実際に発明されました。@autorelease古いものよりもはるかに効率的であることが示されていますが、このNSAutoReleasePoolような状況では避ける必要があります。

NSString2つ目は、新しいのソース文字列としての文字列リテラルの処理に関しては、かなり未知の最適化NSStringです。この回答で説明されているように、を使用initWithString:@"SomeLiteral"すると、新しいの形成ではなく、リテラルへのポインターが生成されNSStringます。この最適化はに適用されませんstringWithFormat:。つまり、あなたはあなたのテストを歪めました。

ノート:

開始時間を節約した後に呼び出す[super viewDidAppear:animated];と、テストが不必要に歪められます。

私がこれらのテストを実行したとき、それらはプロファイラーで次の時間に実行されました。

  • 例1:0.999
  • 例2:1.526秒
  • 例3:1.082s

2秒弱から80秒にどのように進んだかわかりません。

于 2012-11-25T16:22:02.500 に答える
1

学習の精神で、私はあなたにただのヒントを与えます。NSMutableStringおよびに関するドキュメントを参照してNSAutoreleasePoolください。

于 2012-11-25T15:54:30.840 に答える