retain cycle inside of block with local scope ivars

181 views Asked by At

For the life of me, I cannot figure out what's going on here.

As an overview, I have an application that I've created a custom navigation bar, a custom containment view controller, and callbacks to tell me when expensive processes have been finished executing inside individual view controllers (such as a large set of images).

Inside my containment view controller when navigating to a child view controller, a call back is set on the child view controller to call the transition animation after it has finished it's set of expensive processes.

The callbacks are created as such

@interface UIViewController (CallBack)

typedef void (^CompletionBlock)(void);

@property (nonatomic, copy) CompletionBlock callBackBlock;

- (void)doneLoadingImages;

@end

static char const *const CompletionBlockTagKey = "CompletionBlockTag";

@implementation UIViewController (CallBack)
@dynamic callBackBlock;

- (CompletionBlock)callBackBlock
{
    return objc_getAssociatedObject(self, CompletionBlockTagKey);
}

- (void)setCallBackBlock:(CompletionBlock)callBackBlock
{
    objc_setAssociatedObject(self, CompletionBlockTagKey, callBackBlock, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (void)doneLoadingImages
{
    [self callBackBlock]();

    self.callBackBlock = nil;
}

@end

These callbacks are set before the child view controller is added through addChildViewcontroller:, and are fired in a dispatch_async block such as this

__block __weak ThisUIViewController *me = self;

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    UIImage *image1 = [UIImage imageForNibFromFileName:@"picture_name"];

    dispatch_async(dispatch_get_main_queue(), ^{
        me.imageViewToSet.image = image1;

        [me doneLoadingImages];
    });
});

This process goes through fine the first time a UIViewcontroller gets called from my container view controller through my navigation code. According to the profiler, it dumps memory properly as well after I navigate away from it indicating my retain count is 0 for it.

BUT, what happens when I navigate to the same UIViewcontroller again is that the images are loaded super fast, and the doneLoadingImages gets called super fast as well, causing hang ups on my main thread and causing the UI to become unresponsive until everything has been set UI wise.

My imageForNibFromFileName: method is just a convenience category method that uses imageForContentsOfFile: internally. Anybody have some insight on what I may be doing wrong here?

1

There are 1 answers

0
dannyskim On BEST ANSWER

Turns out it wasn't a retain issue. I'm not exactly sure why, but I had to separate the images into their own dispatch_async call from the global_queue instead of chaining them all in one async block. If anybody has an explanation of why this has to be done or what's going on in the background, that would be appreciated.