Question

I'm trying to fetch remote web images using NSOperation and completion blocks. Essentially, the receiving object (view controller) will call the SGImageManager's fetchImageWithUrlString:completionBlock method, which in turn will setup an SGFetchImageOperation that has it's own completion block. In the end, the operation invokes a completion block within a completion block.

The app doesn't crash, but it repeatedly breaks on the line indicated and in the inspector, there are strange values associated with operationImage and operationUrlString. I'm not sure how to debug this. The only theory I have is there are circular calls happening for some reason.

//SGFetchImageOperation.h
typedef void(^SGFetchImageCompletionBlock)(UIImage *image, NSString *urlString);

@interface SGFetchImageOperation : NSOperation
@property (nonatomic, strong) NSString *urlString;
@property (copy) SGFetchImageCompletionBlock completionBlock;
@end


//SGFetchImageOperation.m
#import "SGFetchImageOperation.h"

@implementation SGFetchImageOperation

- (void)main {
    @autoreleasepool {
        if (self.isCancelled) {
            return;
        }

        UIImage *image = [self image];

        if (self.isCancelled) {
            return;
        }

        if(self.completionBlock  &&  self.urlString  && image) {
            dispatch_async(dispatch_get_main_queue(), ^{
                self.completionBlock(image, self.urlString);
            });
        }
    }
}

- (UIImage *)image{
    UIImage *image;
    if(self.urlString){
        NSURL *url = [NSURL URLWithString:self.urlString];
        NSError *error = nil;
        NSData *data = [NSData dataWithContentsOfURL:url options:NSDataReadingMappedAlways error:&error];
        if (data) {
            image = [UIImage imageWithData:data];
        } else {
            NSLog(@"Error downloading image. %@", error.localizedDescription);
        }
    }
    return image;
}

@end



//SGImageManager.h
#import "SGFetchImageOperation.h"

@interface SGImageManager : NSObject
- (void)fetchImageWithUrlString:(NSString *)urlString completionBlock:(SGFetchImageCompletionBlock)completionBlock;
@end


//SGImageManager.m
- (void)fetchImageWithUrlString:(NSString *)urlString completionBlock:(SGFetchImageCompletionBlock)completionBlock {
    SGFetchImageOperation *operation = [SGFetchImageOperation new];
    operation.urlString = urlString;

    //Keeps breaking on this line with "Thread x: EXC_BAD_ACCESS (code=2, address=0x1)", but doesn't seem to crash.
    operation.completionBlock = ^(UIImage *operationImage, NSString *operationUrlString){

        completionBlock(operationImage, operationUrlString);
    };
    [self.queue addOperation:operation];
}
Was it helpful?

Solution

I think the problem here is you're adding a property called completionBlock to a subclass of NSOperation which already has methods defined for completionBlock.

You could get rid of the property on your subclass, and just use NSOperation's -setCompletionBlock: method.

Alternatively, you could rename your current property to something like SGCompletionBlock

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top