简体   繁体   中英

iPhone SDK background thread image loading problem

I have created a grid view that displays six "cells" of content. In each cell, an image is loaded from the web. There are a multiple pages of this grid (the user moves through them by swiping up / down to see the next set of cells).

Each cell has its own view controller. When these view controllers load, they use an ImageLoader class that I made to load and display an image. These view controllers implement an ImageLoaderDelegate that has a single method that gets called when the image is finished loading.

ImageLoader does its work on a background thread and then simply notifies its delegate when it is done loading, passing the image to the delegate method.

Trouble is that if the user moves on to the next page of grid content before the image has finished loading (releasing the GridCellViewControllers that use the ImageLoaders), the app crashes. I suspect that this is because along the line, an asynchronous method finishes and attempts to notify its delegate but can't because it's been released.

Here's some code to give a better picture:

GridCellViewController.m:

- (void)viewDidLoad {
    [super viewDidLoad];

    // ImageLoader  
    _loader = [[ProductImageLoader alloc] init];
    _loader.delegate = self;

    if(_boundObject)
        [_loader loadImageForProduct:_boundObject]; 
}

//ImageLoaderDelegate method
- (void) imageDidFinishLoading: (UIImage *)image {
    [_imgController setImage:image];
}

ProductImageLoader.m

- (void) loadImageForProduct: (Product *) product {
    // Get image  on another thread
    [NSThread detachNewThreadSelector:@selector(getImageForProductInBackground:) toTarget:self withObject:product];
}

- (void) getImageForProductInBackground: (Product *) product {
    NSAutoreleasePool *tempPool = [[NSAutoreleasePool alloc] init];

    HttpRequestLoader *tempLoader = [[HttpRequestLoader alloc] init];

    NSURL *tempUrl = [product getImageUrl];

    NSData *imageData = tempUrl ? [tempLoader loadSynchronousDataFromAddress:[tempUrl absoluteString]] : nil;

    UIImage *image = [[UIImage alloc] initWithData:imageData];

    [tempPool release];

    if(delegate)
        [delegate imageDidFinishLoading:image];
}

The app crashes with EXC_BAD_ACCESS.

Disclaimer: The code has been slightly modified to focus on the issue at hand.

Half the time I ask a question on here, I come up with a solution shortly after. Writing down the issue is probably 90% of the solution.

Anyway, here's what I did, and there is probably an improvement to be made to this as I'm still suspicious of the threads.

As it turns out the problem was that the delegate method would still fire (imageDidFinishLoading), but the delegate had been released. By explicitly setting the delegate to nil in GridCellViewController.m when it is released, the message never fires.

GridCellViewController.m

- (void) dealloc {
    if(_loader) {
        _loader.delegate = nil;
        [_loader release];
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM