Objective-C重用NSString内存泄漏

Objective-C重用NSString内存泄漏,objective-c,cocoa,memory,memory-leaks,nsstring,Objective C,Cocoa,Memory,Memory Leaks,Nsstring,我编写了一个非常简单的测试应用程序,试图帮助我完成一个更大的项目 简单地说,测试应用程序循环预定次数,并在每个循环上的字符串后面加上“1”。当循环达到1000的倍数时,字符串被重置,过程重新开始 代码如下;但我发现内存使用率比我预期的要高很多。每次迭代都会增加大约0.5MB的内存 新闻字符串似乎没有被重用,而是被丢弃并创建了一个新实例,而没有恢复它所使用的内存 最终,软件的数量需要远远高于100000。 作为测试,如果我将迭代更改为1000万次,它将占用超过5GB的内存 有人有什么建议吗?到目前

我编写了一个非常简单的测试应用程序,试图帮助我完成一个更大的项目

简单地说,测试应用程序循环预定次数,并在每个循环上的字符串后面加上“1”。当循环达到1000的倍数时,字符串被重置,过程重新开始

代码如下;但我发现内存使用率比我预期的要高很多。每次迭代都会增加大约0.5MB的内存

新闻字符串似乎没有被重用,而是被丢弃并创建了一个新实例,而没有恢复它所使用的内存

最终,软件的数量需要远远高于100000。 作为测试,如果我将迭代更改为1000万次,它将占用超过5GB的内存

有人有什么建议吗?到目前为止,我有各种各样的方法来清除字符串,关闭ARC并手动释放它/重新创建,但似乎没有一种方法能够回收我所期望的内存量

谢谢你的帮助

*是的,这个实际的软件是完全没有意义的,但正如我所说,它是一个测试应用程序,一旦修复,它将被移植到一段有用的代码中


int targetCount=100000;
NSString*新闻字符串;
int main(int argc,const char*argv[]{
@自动释放池{
过程();
返回0;
}
}
无效过程(){

对于(int i=0;iYour
calledFunction
函数创建一个自动释放的
NSString
,该函数在当前自动释放池耗尽之前不会释放

您的
process
函数在循环中调用
calledFunction
100000次。在此循环期间,当前自动释放池没有被释放的机会。在
process
方法结束时,在
calledFunc中创建的
NSString
对象的所有100000个实例都将被释放选项
仍在内存中

使用大循环时,避免自动释放对象堆积的常见解决方案是添加额外的自动释放池,如下所示:

void process() {
    for (int i=0; i<targetCount; i++) {
        @autoreleasepool {
            calledFunction(i);
        }
    }
}
void进程(){

对于(inti=0;i您的问题源于自动释放池,在ARC时代,这是一个有点过时的功能

当使用
alloc
/
init
组合创建对象时,生成的对象归调用方所有。对于标准
new
方法也是如此,它定义为
alloc
,后跟
init

对于类所使用的每个
init…
方法,都有一个匹配的
..
方法,这被定义为
alloc
/
init
/
autorelease
并将一个无主对象返回给调用方。例如,您的代码使用
stringWithFormat:
,它被定义为
alloc
/
initWithFormat
自动释放

未拥有的返回对象位于自动释放池中,除非取得所有权,否则下次清空该池时将自动释放该对象,对于主自动释放池,主事件循环每次迭代一次。在许多程序中,事件循环的迭代次数足以从自动释放池中回收对象ol足够快,内存使用率不会上升。但是,如果在事件循环的一次迭代中创建了大量对象,然后丢弃了大量对象,例如在大型
for
循环中,自动释放池在清空之前可能会有大量需要的对象

通用解决方案

此问题的一个常见解决方案是使用本地自动释放池,该池在退出后立即清空。通过明智地放置此类本地自动释放池,可以最大限度地减少内存使用。它们的一个常见位置是包装产生大量垃圾的循环体,在您的代码中:

void process()
{
   for (int i=0; i<targetCount; i++)
   {  @autoreleasepool
      {
         calledFunction(i);
      }
   }
}
(由于变量参数的原因,这比大多数新的
族方法更复杂。)

将上述内容添加到您的应用程序中,然后将对
stringWithFormat
的调用替换为
newWithFormat
,返回的字符串将归调用者所有,ARC将管理它们,它们不会填满自动释放池-它们永远不会进入该池,并且您无需确定将
@autoreleasepool
c放置在何处建设。赢,赢,赢:-)


HTH

如果您在代码的更深层使用更多的
@autoreleasepool
作用域(即
过程()的
循环的
内),会发生什么?说真的?这就解决了?!哈哈。特洛伊人,你是我的新英雄!是的,这只是一个自动释放池被耗尽的频率的问题。然而,无论如何,这不是一个很好的使用模式;请看
NSMutableString
。我尝试了mutablestring作为失败的尝试之一,但这似乎占用了更多的内存。我现在我有了一个字符串的金块,我从来没有想过它会沿着函数向下传播。噢,每次调用
stringWithFormat
,你都在告诉运行时创建一个新实例。
void process()
{
   for (int i=0; i<targetCount; i++)
   {  @autoreleasepool
      {
         calledFunction(i);
      }
   }
}
@interface NSString (ARC)

+ (instancetype)newWithFormat:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2);

@end

@implementation NSString (ARC)

+ (instancetype)newWithFormat:(NSString *)format, ...
{
   va_list args;
   va_start(args, format);
   id result = [[self alloc] initWithFormat:format arguments:args];
   va_end(args);
   return result;
}

@end