0

为了测试内存管理和分配,我编写了一个简单的 Single View 应用程序,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. 为什么在这三种情况下分配的总内存没有变化(尽管在第二种和第三种情况下没有内存泄漏),有没有办法减少大分配的内存?故障在哪里,如何减少内存消耗?

  2. 其次,我是否使用了正确的工具并读取了正确的数字(在 Profiler Instruments 中使用 Memory Leaks,并参考 All Allocations,在分配生命周期中,选择 created and still living),我是否以正确的方式进行操作?我的意思是这是目前消耗的内存量,还是自应用程序启动以来消耗的总内存量?

  3. 为什么在第二种情况下循环执行时间显着增加,而不是在第一种和第三种情况下?

使用 xCode 4.5,没有 ARC,没有 StoryBoards,所有测试都是在模拟器上完成的。

4

2 回答 2

3

1.三种情况的总分配量相同,因为在每种情况下,您分配的内存量相同。

2.你使用了正确的工具,并且阅读了一些正确的数字,我认为你只是没有阅读足够的数字。正如您所怀疑的,总体字节本质上不是您当前消耗的。您需要检查Live Bytes部分。在本节中,您将看到第一种情况(浪费了近一半的总分配)与第二种情况之间的区别,后者最终处于相当小的状态。

3. “显着”的时间增加来自使用之间的差异:

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

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

是的,我说的是差异。而不仅仅是autorelease. 但...

众所周知,首次自动释放在循环释放内存方面效率低下。从一开始就不是它的真正目的,它只是真正发明出来的,以便方法可以返回对象的调用者所拥有的对象,换句话说:它的设计目的是让事情活得太久。虽然@autorelease已被证明比旧NSAutoReleasePool的 s 更有效,但在这种情况下应该避免使用它。

NSString第二个是在处理字符串文字作为新的源字符串时所做的一个相当未知的优化NSString。如本答案中所述, UsinginitWithString:@"SomeLiteral"会导致指向文字的指针,而不是形成 new NSString。此优化不适用于stringWithFormat:. 所以换句话说,你已经扭曲了你的测试。

笔记:

在保存开始时间后调用[super viewDidAppear:animated];会不必要地扭曲测试。

当我运行这些测试时,他们在分析器中运行了这些时间:

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

我不确定你是如何从不到 2 秒到 80 秒的。

于 2012-11-25T16:22:02.500 回答
1

本着学习的精神,我只是给你一个提示。浏览关于NSMutableString和的文档NSAutoreleasePool

于 2012-11-25T15:54:30.840 回答