【问题标题】:Objective-c memory managementObjective-c 内存管理
【发布时间】:2011-05-09 21:39:37
【问题描述】:

作为 Objective-c 及其内存管理技术的新手,比较以下两部分。 (原代码来自apple.com autorelease pools
问题:
1. 两片能达到同样的效果吗?
2. 两块内存管理的结果是一样的吗? (至于内存清理、泄漏等)
3. 下面的第二个代码是否违反了最佳实践?性能?

void main()
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSArray *args = [[NSProcessInfo processInfo] arguments];
    for (NSString *fileName in args) {
        NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];
        NSError *error = nil;
        NSString *fileContents = [[[NSString alloc] initWithContentsOfFile:fileName
                                           encoding:NSUTF8StringEncoding error:&error] autorelease];
        /* Process the string, creating and autoreleasing more objects. */
        [loopPool drain];
    }
    /* Do whatever cleanup is needed. */
    [pool drain];
    exit (EXIT_SUCCESS);
}

void main()
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSArray *args = [[NSProcessInfo processInfo] arguments];
    NSString *fileContents = [[NSString alloc] autorelease]; 
    for (NSString *fileName in args) 
    {
        // NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];
        NSError *error = nil;
        fileContents = [fileContents initWithContentsOfFile:fileName encoding:NSUTF8StringEncoding error:&error];
        /* Process the string, creating and autoreleasing more objects. */
        //[loopPool drain];
    }
    /* Do whatever cleanup is needed. */
    [pool drain];
    exit (EXIT_SUCCESS);
}

【问题讨论】:

  • 第二个绝对是非常规的,而且很可能是错误的;您分配一块内存,使用该内存初始化一个对象,将内存标记为释放,然后再次使用它来初始化一个新对象,在重新标记它的过程中。
  • @Josh,不是这样 - 他正在做的很好。除了变量之外没有任何东西被重用,它只是在每次迭代时指向不同的内存位置。
  • 我在第 2 部分中为 fileContents 放置“自动释放”的位置出错。我编辑了代码以反映更改。
  • 第二个例子仍然不正确——你不能用+alloc创建一个对象,然后重复调用它的-init方法之一。您必须每次都将+alloc-init... 作为一对呼叫在一起。
  • 正如 Sherm 所说,第二个例子是完全错误的。这种模式毫无意义。

标签: objective-c memory-management


【解决方案1】:
  1. 这两部分的结果是否相同?

没有。当第二次调用fileContents = [fileContents ... 时,它肯定会爆炸和/或泄漏。实现假定其实例的内存为零,因为这是由运行时保证的。

即使一个初始化程序被证明可以正常工作而没有问题或泄漏,其中许多也不会。

不要尝试像这样“更新”您的对象 - 请以正常方式进行 =)

序列(在非 gc 环境中)应为:1)alloc2)init...(使用指定的初始化程序)3)dealloc

这两个部分实现相同的内存管理结果吗? (至于内存清理、泄漏等)

没有。除了 #1 中概述的内容之外,您不应假设从 alloc 返回的地址与从初始化程序返回的地址相同。初始化器可以选择销毁alloc 的结果并创建或返回另一个分配。在这种情况下,您将引入引用计数不平衡(效果:泄漏和/或僵尸)。

下面的第二个代码是否违反了最佳实践?

NSString *fileContents = [[NSString alloc] autorelease]; 很奇怪,非常规。第一次阅读时,我错过了错误,因为将init...] 放在allocautorelease 之间太传统了。

下面的第二个代码是否违反了性能方面的任何规定?

从内部循环中移除自动释放池可能会影响性能。当发生大分配和/或高迭代时,最好创建小的本地化池,就像原来的那样。

【讨论】:

  • [[[NSString alloc] init] autorelease]的理由也是零——这只是一个空的、不可变的字符串,所以你不妨使用@""
【解决方案2】:

回答您的问题:

1) 这两部分是否达到相同的结果?

不,他们没有,主要是因为第二个例子是不正确的,在两个帐户上。

  1. 首先,NSString *fileContents = [[NSString alloc] autorelease] 意义不大。这一行实质上为字符串分配空间,甚至没有初始化它(并将其初始保留计数增加到 1),告诉它把自己放到一个自动释放池中。这是一个很大的禁忌。
  2. 其次,fileContents = [fileContents initWithContentsOfFile:fileName encoding:NSUTF8StringEncoding error:&error] 也没什么意义。您正在使用尚未以任何方式释放或重置的旧内存初始化一个新对象,这只是未定义的行为。正如 Sherm Pendley 所提到的,您不能一次分配空间并简单地重用该内存而无需担心,并且由于您不知道内部如何定义 NSString,这可能会简单地泄漏内存或非常简单地炸毁您的脸。另一个大禁忌。

代码的其余部分似乎没问题(它确实取决于您在“流程”步骤中所做的事情)。正如其他人所指出的,+alloc-init 几乎总是相互配对,但这不仅仅是因为惯例,而是因为这种配对是正确的做法。您必须先+alloc 内存才能开始使用它,并且您必须-init 使内存归零并在正确的条件下正常运行。任何其他组合都可能不适合使用。

2) 这两块内存管理结果是一样的吗? (至于内存清理、泄漏等)

不,他们绝对不会。第二个将泄漏大量内存,甚至可能无法运行。第一次肯定没问题,但由于使用了内部自动释放池,可能无法有效运行。

3) 下面的第二个代码是否违反了最佳实践?表现?

是的,确实如此,事实上,即使是第一个也可能无法有效运行。创建内部自动释放池可能不符合您的最佳利益,甚至可能会阻碍您的程序。可敬的Mike Ash 对自动释放池进行了一些测试,results 有点令人惊讶。事实证明,不同大小的自动释放池在不同的计算机上会更好地工作(当时,在他的旧计算机上,每个池 100 个对象是最有效的;对我来说,10000 个对象的效果要好一点,即使那样也只是稍微好一点超过 1000 个对象)

【讨论】:

    猜你喜欢
    • 2020-01-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-08
    • 2011-05-11
    相关资源
    最近更新 更多