DO循环和方便的方法会导致ARC内存峰?(Do loops and convenience meth

2019-07-30 21:30发布

我与ARC合作,在一个循环中修改字符串时,看到一些奇怪的行为。

在我的情况,我使用的NSXMLParser委托回调循环,但我看到使用演示项目和示例代码,只需修改一些完全相同的行为和症状NSString对象。

您可以从GitHub下载演示项目 ,要求在主视图控制器的四个方法,只是取消注释其中viewDidLoad方法来测试不同的行为。

为简单起见,这里有一个简单的循环,我已经坚持到空的单一视图的应用程序。 我直接粘贴此代码到viewDidLoad的方法。 它运行出现的观点之前,所以屏幕是黑的,直到循环结束。

NSString *text;

for (NSInteger i = 0; i < 600000000; i++) {

    NSString *newText = [text stringByAppendingString:@" Hello"];

    if (text) {
        text = newText;
    }else{
        text = @"";
    }
}

下面的代码还保留吃内存,直到循环完成:

NSString *text;

for (NSInteger i = 0; i < 600000000; i++) {

    if (text) {
        text = [text stringByAppendingString:@" Hello"];
    }else{
        text = @"";
    }
}

下面是这两个环路的像仪器,与分配工具运行:

看到? 渐进而稳定的内存使用情况,直到内存不足警告的一大堆,然后应用模具,自然。

接下来,我试过的东西有点不同。 我用的一个实例NSMutableString ,就像这样:

NSMutableString *text;

for (NSInteger i = 0; i < 600000000; i++) {

    if (text) {
        [text appendString:@" Hello"];
    }else{
        text = [@"" mutableCopy];
    }
}

此代码似乎工作好了很多,但仍然崩溃。 这里是什么样子:

接下来,我想这在一个较小的数据集,以查看是否不循环可以生存积聚足够长的时间来完成。 这里的NSString版本:

NSString *text;

for (NSInteger i = 0; i < 1000000; i++) {

    if (text) {
        text = [text stringByAppendingString:@" Hello"];
    }else{
        text = @"";
    }
}

它崩溃以及,将得到的存储器图形看起来类似于使用此代码生成的第一个:

使用NSMutableString ,同样的百万迭代循环不仅成功,但它少了很多时间做。 下面的代码:

NSMutableString *text;

for (NSInteger i = 0; i < 1000000; i++) {

    if (text) {
        [text appendString:@" Hello"];
    }else{
        text = [@"" mutableCopy];
    }
}

而看看内存使用情况图:

在开头的短穗是通过环路引起的内存使用情况。 还记得当我指出,看似不相关的事实是,屏幕是循环的处理过程中的黑色,因为我在viewDidLoad中运行呢? 该穗后立即出现的视图。 所以看来不仅做到NSMutableStrings在这种情况下更有效地处理内存,但他们也快得多。 迷人。

现在,回到我的实际情况......我使用NSXMLParser解析出的API调用的结果。 我创建Objective-C对象符合我的XML响应结构。 因此,考虑例如,看起来像这样的XML响应:

<person>
<firstname>John</firstname>
<lastname>Doe</lastname>
</person>

我的目标是这样的:

@interface Person : NSObject

@property (nonatomic, strong) NSString *firstName;
@property (nonatomic, strong) NSString *lastName;

@end

现在,在我的NSXMLParser委托,我会继续前进,依次通过我的XML,我会跟踪当前的元素(我并不需要一个完整的层次表达,因为我的数据是相当平坦,它的转储MSSQL数据库,XML),然后在foundCharacters方法,我跑是这样的:

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
  if((currentProperty is EqualToString:@"firstname"]){
    self.workingPerson.firstname = [self.workingPerson.firstname stringByAppendingString:string]; 
  }
}

此代码是很像的第一个代码。 我有效地通过使用XML循环NSXMLParser ,所以如果我是记录了我所有的方法调用,我会看到这样的事情:

parserDidStartDocument:解析器:didStartElement:的namespaceURI:的qualifiedName:属性:解析器:foundCharacters:解析器:didStartElement:的namespaceURI:的qualifiedName:解析器:didStartElement:的namespaceURI:的qualifiedName:属性:解析器:foundCharacters:解析器:didStartElement:的namespaceURI:的qualifiedName:解析器:didStartElement:的namespaceURI:qualifiedName中:属性:解析器:foundCharacters:解析器:didStartElement:的namespaceURI:qualifiedName中:parserDidEndDocument:

看到这个模式? 这是一个循环。 需要注意的是它可能有多个连续的调用parser:foundCharacters:为好,这就是为什么我们追加属性以前的值。

把它包起来,这里有两个问题。 首先,内存任何一种循环的建立似乎崩溃的应用程序。 其次,使用NSMutableString与性能也不是那么优雅,我甚至不能确定它的工作如预期。

在一般情况下,有什么办法来克服这种记忆积累,同时通过字符串中使用ARC循环? 是否有特定的NSXMLParser东西,我可以做什么?

编辑:

初步测试表明,即使使用第二@autoreleasepool{...}似乎并没有解决问题。

这些对象必须从某个地方去记忆,而thwy存在,而且它们还在那里,直到runloop结束,当自动释放池可能会耗尽。

这不固定的字符串任何情况下尽可能的NSXMLParser推移,它可能会,因为循环分布在方法调用 - 需要进一步检验。

(请注意,我把这种记忆高峰,因为从理论上讲,ARC会在某个时候清理内存,只是没有,直到它达到峰值后出来,没有什么实际泄漏,但它具有相同的效果。)

编辑2:

坚持循环内的自动释放池有一些有趣的效果。 它似乎几乎减轻累积追加到时NSString对象:

NSString *text;

for (NSInteger i = 0; i < 600000000; i++) {

        @autoreleasepool {
            if (text) {
                text = [text stringByAppendingString:@" Hello"];
            }else{
                text = [@"" mutableCopy];
            }
        }
    }

拨款痕迹看起来像这样:

我注意到的记忆随着时间的推移逐渐积累,但它是大约150千字节,而不是350兆字节前面看到的曲调。 但是,此代码,使用NSMutableString相同的行为,因为它没有不带自动释放池:

NSMutableString *text;

for (NSInteger i = 0; i < 600000000; i++) {

        @autoreleasepool {
            if (text) {
                [text appendString:@" Hello"];
            }else{
                text = [@"" mutableCopy];
            }
        }
    }

和分配跟踪:

这样看来,是的NSMutableString显然免疫的自动释放池。 我不知道为什么,但在第一个猜测,我会配合这与我们前面看到的,是NSMutableString可以处理大约在自己的一百万次迭代,而NSString不能。

那么,什么是解决这个的正确方法是什么?

Answer 1:

您正在污染与吨,吨自动释放对象的自动释放池。

环绕具有自动释放池循环的内部部分:

for (...) {
    @autoreleasepool {
        ... your test code here ....
    }
}


Answer 2:

当你打猎的内存相关的错误,你应该注意的是@“”和@“您好”将是不朽的对象。 你可以把它作为一个常量,但是对于对象。 将有一个且只有一个,这个对象在内存中的整个时间的实例。

作为@bbum指出,与你验证,@autoreleasepool是对付这种在一个循环的正确方法。

在与@autoreleasepool和你的NSMutableString例如,池并没有真正做多。 循环中的唯一致命的对象是你的mutableCopy @“”,但将只能使用一次。 另一种情况是只是一个objc_msgSend到持久对象(的NSMutableString),其仅引用一个不朽对象和一个选择器。

我只能假设内存堆积是苹果公司的执行的NSMutableString里面,虽然我不知道为什么你会看到它的@autoreleasepool内,而不是当它是不存在的。



文章来源: Do loops and convenience methods cause memory peaks with ARC?