sam-w
sam-w

Reputation: 7687

Understanding ViewController dealloc process

I have a container UIViewController which does the following when removing one of its children:

- (void)removeChildWithIndex:(NSUInteger)Index {
  @autoreleasepool {
    ChildViewController *child = [_children objectAtIndex:Index];

    //Remove the child from the VC hierarchy
    [child willMoveToParentViewController:nil];
    [child.view removeFromSuperview];
    [child removeFromParentViewController];

    //Remove the child from array
    [_children removeObjectAtIndex:Index];
  }

  //Post a notification for anyone who might care
  [[NSNotificationCenter defaultCenter] postNotificationName:RemovedChildNotification object:self];
}

The root of my problem is that child is not being dealloced at the end of the @autoreleasepool block, but instead is released a little bit later (by the looks of it after the RunLoop has a chance to process an internal list of outstanding events):

enter image description here

This ordinarily wouldn't be a problem, but one object which is observing the NSNotification sent out at the end of the function above is relying on the child being dealloced before it receives the notification.

Can anybody explain/link me to some documentation to help me understand why child isn't released immediately?

Alternatively, if I have no choice about when child is dealloced, can anybody suggest a clean way of delaying my notification until after the dealloc? I suppose I could put a call in [ChildViewController dealloc] to inform the parent of its demise and fire off the notification at that point, but that's a pretty dirty way of doing it...

Upvotes: 3

Views: 1674

Answers (2)

datwelk
datwelk

Reputation: 1037

Try sending the notification in the next runloop iteration:

- (void)removeChildWithIndex:(NSUInteger)Index 
{
    ChildViewController *child = [_children objectAtIndex:Index];

    //Remove the child from the VC hierarchy
    [child willMoveToParentViewController:nil];
    [child.view removeFromSuperview];
    [child removeFromParentViewController];
    [child didMoveToParentViewController:nil];

    //Remove the child from array
    [_children removeObjectAtIndex:Index];

    //Post a notification for anyone who might care
    [self performSelector:@selector(_postRemoveChildNotification) withObject:nil afterDelay:0.0f];
}

- (void)_postRemoveChildNotification 
{
    [[NSNotificationCenter defaultCenter] postNotificationName:RemovedChildNotification object:self];
}

Upvotes: 2

danypata
danypata

Reputation: 10175

The main problem in your code is the autoreleasepool whcih

Autorelease pool blocks provide a mechanism whereby you can relinquish ownership of an object, but avoid the possibility of it being deallocated immediately

So basically the autoreleasepool will help you removing the ownership of the objects inside the autorelease block but will guarantee that they are not release immediately.

So what you have to do is remove the autoreleasepool block and let your code as is and at the bottom of your code you can also make the child nil child = nil this will also help your class that receive the notification.

Also don't write code that will base on the fact that some objects will be deallocated when your code is executed. The SO can't guarantee that one object will be deallocated immediately after a method is called. This is just a bad practice, if you can you should rethink your implementation.

Upvotes: 0

Related Questions