Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Forcing an object to deallocate under ARC

I'm working on an iPad photo collage app that draws perhaps hundreds of UIImageViews on the screen at once.

There is a button that lets the user "re-create", which is suppose to run a for loop to [photo removeFromSuperview] on all photos and then initialize a new batch, in that order.

I'm using ARC, and my console tells me that my Photo's dealloc method isn't being called until AFTER the next batch has been drawn, meaning I'm running into memory issues, even though I'm trying to remove the first set before adding the next set.

Is there a way to either 1) wait until all the photos have been properly dealloc'd or 2) force all the photos to dealloc immediately under ARC?

like image 521
user339946 Avatar asked Oct 10 '12 19:10

user339946


1 Answers

You are probably putting your image views in an autorelease pool without realizing it. You may be able to fix this by wrapping your own autorelease pool around your for-loop.

For example, I made a very simple test project with one image view and one button under my top-level view. When I tap the button, it removes the image view and creates a new one. It removes the image view by looping over the top-level view's subviews. Here's the code:

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self initImageView];
}

- (IBAction)redoWasTapped:(id)sender {
    [self destroyImageView];
    [self initImageView];
}

- (void)destroyImageView {
    for (UIView *subview in self.view.subviews) {
        if ([subview isKindOfClass:[UIImageView class]]) {
            [subview removeFromSuperview];
        }
    }
}

- (void)initImageView {
    UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"picture.jpg"]];
    imageView.frame = CGRectInset(self.view.bounds, 100, 100);
    [self.view addSubview:imageView];
}

@end

When I ran this under the Allocations instrument with “Record reference counts” enabled, I saw that each removed image view was not deallocated during destroyImageView. Instead, it was deallocated later when the run loop called -[NSAutoreleasePool release].

Then I changed destroyImageView to manage its own autorelease pool:

- (void)destroyImageView {
    @autoreleasepool {
        for (UIView *subview in self.view.subviews) {
            if ([subview isKindOfClass:[UIImageView class]]) {
                [subview removeFromSuperview];
            }
        }
    }
}

When I ran it again under Instruments, I saw that each removed image view was deallocated during destroyImageView, at the end of the @autoreleasepool block.

like image 155
rob mayoff Avatar answered Oct 29 '22 00:10

rob mayoff