Extraña pérdida de memoria en la ventana: addSubView

first of all sorry for my English :-) not so good.

I have a strange memory leak with the following code (code after the explanation). I have a class, FLWaitingView. It is a simple view with a waiting indicator (plus a view with background), used to say to the user "wait for the data to be loaded". It has two simple methods: show and dismiss. In the show method, I find the main Application Window and add the subviews (the waiting view and a background view, with different animations). In the dismiss method, I remove it from superview. In every show, I verify that the view isn't already visible using a static bool var (is_visible).

The strange thing is this: In the dismiss method, I use:

[self.view removeFromSuperview];
[self.waitingView removeFromSuperview]; 

to remove the two views from the Window, to avoid them to be retained. They are correctly removed, I can verify this with NSLog (for cicle on each window subview). But, in INSTRUMENTS, using the "mark heap" function, I see that in every single reload (new instance of FLWaitingView, then show, then dismiss) the old instance remains in memory and continues to increase memory usage. Obviously is not a problem of the calling code, because I correctly release the object:

//CALLING CODE
//customWaitingView is a property retained
self.customWaitingView = [[[FLWaitingView alloc]init]autorelease];
[self.customWaitingView show];

Moreover, and I think that this is the most important information, if I move the view dismission in another method, called by a selector, the leak disappear!!!

Now I show the "wrong" code and, after, the "correction". I would like to understand why it happens.

- (void)show
{

    if (!is_visible){

        id appDelegate = [[UIApplication sharedApplication] delegate];
        UIWindow *window = [appDelegate window];
        self.waitingLabel.text = @"Attendere";

        self.view.alpha = 1.0;
        self.waitingView.alpha = 1.0;

        [window addSubview:self.view];
        [window addSubview:self.waitingView];
        [self.waitingIndicator startAnimating];
        self.view.frame = window.frame;
        self.waitingView.center = window.center;
        // "Pop in" animation for alert
        [self doPopInAnimationWithDelegate:self];
        // "Fade in" animation for background
        [self doFadeInAnimation];
        is_visible = YES;
    } else {
        NSLog(@"FLWaitingView %@ already visible, do nothing", self);
    }

}


- (void)dismiss
{
    [UIView beginAnimations:nil context:nil];
    self.view.alpha = 0.0;
    self.waitingView.alpha = 0.0;
    [UIView commitAnimations];
    [self.waitingIndicator stopAnimating];

    //here is the problem
    [self.view removeFromSuperview];
    [self.waitingView removeFromSuperview]; 
    is_visible = NO;
}

the code above is the "wrong" one, but if I add

[self performSelector:@selector(alertDidFadeOut) withObject:nil afterDelay:0.5];

in the dismiss method and a new method (obviously removing the redundant code from dismiss method):

- (void)alertDidFadeOut
{      
    //here the memory is correctly released
    [self.view removeFromSuperview];
    [self.waitingView removeFromSuperview];
    is_visible = NO;
}

the memory is correctly released. Why?????? Thank you in advance

Fabio

preguntado el 10 de marzo de 12 a las 12:03

1 Respuestas

Your view isn't getting released as you would be expecting because at the moment you're releasing it there are still animations linked to it. You can only properly release it after the animations are finished.

Your second method works because the animation lasts less than 0.5 seconds - the releasing code is called after view is freed of all the animations.

Proper way to animate the view would be to either create an animation and assign its delegate or maybe a bit more elegant soulution is to use block-based animation like this:

- (void)dismiss
{

    [[UIApplication sharedApplication] beginIgnoringInteractionEvents];                             

    [UIView animateWithDuration: 0.15
        animations: ^{
            self.view.alpha = 0.0;
            self.waitingView.alpha = 0.0;
                     }
        completion: ^(BOOL finished){
            [self.waitingIndicator stopAnimating];
            [self.view removeFromSuperview];
            [self.waitingView removeFromSuperview]; 
            is_visible = NO;    
            [[UIApplication sharedApplication] endIgnoringInteractionEvents];                             
        }];   
}

respondido 10 mar '12, 14:03

this works too (no memory leak), like my other "workaroud", but I don't understand why :-). What is wrong in the first code? the views seems to be correctly removed from main window...but not released... - LombaX

Thanks a lot! Now I understand :-) moving to the block based animation!! - LombaX

@LombaX: no problem! :) but if this is a new project that you're working on you might want to consider using ARC - it'll make your coding life much easier. You'd still need to remove the view de su superview but you can (you actually have to) forget about using retain, release y autorelease. - Rok Jarc

I know about ARC, but I'm pretty new both with iOS/Objective-C and OO Programming (before this, only VB6 and PHP O_o), in this moment my priority is to improve my theoretical skills and memory management is one of my "requirements" :-) thanks again! - LombaX

@LombaX: You're welcome :-) But if you're new to iOS then that's even one more reason to consider ARC. Memory management issues should be left to those old school iOS developers who feel bad about the time they spend to learn iPhone memory management and now all their hard-earned knowledge is almost useless ;-) - Rok Jarc

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.