Pregunta

I am playing around with NSOperationQueue in order to run some code in the background and have it update a UILabel. Here's the viewDidLoad.

- (void)viewDidLoad
{
  [super viewDidLoad];

  queue = [[NSOperationQueue alloc] init];

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

And here's the method called as the invocation 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];
}

As the loop counts up, and more and more @"%d" NSStrings are created, the memory usage naturally goes up. Once the loop finishes however, the memory doesn't seem to deallocate. I expected the memory to fall as the setText: message uses new instances of NSString and releases the old ones.

If I change the loop condition to i<5000000*2, the memory usage is roughly double by the end – so it's definitely something happening on each iteration causing the leak.

Why is memory leaking here?

EDIT: Forgot to mention that I'm using ARC.

¿Fue útil?

Solución

ARC doesn't remove retain / release / autorelease, it just controls the calling of these methods. You can add your own autorelease pool into your loop to force cleanup as it goes:

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

Otros consejos

Let's try:

- (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];
}

What happening in your loop that you are creating NSString and ARC add it to auto release pool.

not releases the memory(NSString*) immediately , will release later.

One more thing is that, actually performSelectorOnMainThread retains both target and objects.

So best way is that you create nsstring instance after sending it to selector set it to nil so ARC will release it.

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

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

IMO, the suggested approach of @Wain should fix the issue.

But you may also use this:

- (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";
    });
}

Try this

  • (void)counterTask {

    __weak NSString *str;

    for (int i=0; i<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]; }

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top