1

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];
}
abc123
  • 8,043
  • 7
  • 49
  • 80
  • Very first thing I want to say, use of self in block is wrong, create weak pointer to self, other wise it will create retain cycle...? – Adnan Aftab Jan 14 '14 at 13:46
  • if app is not crashing and breaking at a point..! are you sure there is no break point? which making app to break at point and after hitting play button it start running again? – Adnan Aftab Jan 14 '14 at 13:48

1 Answers1

3

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

John Grant
  • 1,679
  • 10
  • 8