在循环中重复分配NSString,同时避免内存泄漏

时间:2014-04-28 09:20:52

标签: ios objective-c memory nsstring

我正在玩NSOperationQueue以便在后台运行一些代码并让它更新UILabel。这是viewDidLoad

- (void)viewDidLoad
{
  [super viewDidLoad];

  queue = [[NSOperationQueue alloc] init];

  NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(counterTask) object:nil];
  [queue addOperation:operation];
}

这是调用调用操作的方法:

- (void)counterTask {
  for (int i=0; i<5000000; i++) {
    if (i % 100 == 0) {
      [self.firstLabel performSelectorOnMainThread:@selector(setText:)
                       withObject:[NSString stringWithFormat:@"%d", i]
                       waitUntilDone:YES];
    }
  }

  [self.firstLabel performSelectorOnMainThread:@selector(setText:) withObject:@"finished." waitUntilDone:NO];
}

随着循环数量的增加,越来越多的@"%d" NSString被创建,内存使用率自然会上升。然而,一旦循环结束,内存似乎没有解除分配。由于setText:消息使用NSString的新实例并释放旧实例,我预计内存会下降。

如果我将循环条件更改为i<5000000*2,则内存使用量在结束时大约是两倍 - 所以它肯定会在每次迭代时发生,从而导致泄漏。

为什么内存泄漏?

编辑:忘记提及我正在使用ARC。

5 个答案:

答案 0 :(得分:2)

ARC不会删除retain / release / autorelease ,它只是控制这些方法的调用。您可以将自己的自动释放池添加到循环中以强制清理:

for (int i=0; i<5000000; i++) {
    if (i % 100 == 0) {
        @autoreleasepool {
            [self.firstLabel performSelectorOnMainThread:@selector(setText:)
                                              withObject:[NSString stringWithFormat:@"%d", i]
                                           waitUntilDone:YES];
        }
    }
}

答案 1 :(得分:0)

试试吧:

- (void)counterTask {
@autoreleasepool {
    for (int i=0; i<5000000; i++) {
        if (i % 100 == 0) {
            [self.firstLabel performSelectorOnMainThread:@selector(setText:)
                                              withObject:[NSString stringWithFormat:@"%d", i]
                                           waitUntilDone:YES];
        }
    }
}
[self.firstLabel performSelectorOnMainThread:@selector(setText:) withObject:@"finished." waitUntilDone:NO];
}

答案 2 :(得分:0)

您正在循环中创建NSString并将ARC添加到自动发布池中。

不立即释放内存(NSString *),稍后会释放。

还有一件事是,实际上performSelectorOnMainThread保留了目标和对象。

所以最好的方法是在将它发送到选择器之后创建nsstring实例,将其设置为nil,这样ARC就会释放它。

NSString* strText=[[NSString alloc]initWithFormat:@"%d",i ];

    [self.firstLabel performSelectorOnMainThread:@selector(setText:)
                                      withObject:strText
                                   waitUntilDone:YES];
    strText=nil;

答案 3 :(得分:0)

IMO,建议的@Wain方法应该解决这个问题。

但你也可以用这个:

- (void)counterTask {
    assert([NSThread currentThread] != [NSThread mainThread]);
    for (int i=0; i<5000000; i++) {
        if (i % 100 == 0) {
            dispatch_sync(dispatch_get_main_queue(), ^{
                @autoreleasepool {
                    self.firstLabel.text = [NSString stringWithFormat:@"%d", i];
                }
            });
        }
    }
    dispatch_async(dispatch_get_main_queue, ^{
        self.firstLabel.text = @"finished";
    });
}

答案 4 :(得分:0)

试试这个

  • (void)counterTask {

    __弱NSString * str;

    for(int i = 0; i&lt; 50000; i ++){     if(i%100 == 0){

        str = [NSString stringWithFormat:@"%d", i];
        [self.logInTxtField performSelectorOnMainThread:@selector(setText:)
                                          withObject:str
                                       waitUntilDone:YES];
    }
    

    }

    [self.logInTxtField performSelectorOnMainThread:@selector(setText :) withObject:@“finished”。 waitUntilDone:NO]; }