Question

I'm looking for a solution that solves the following problem:

I have a NSOperation which download the image in the background:

@protocol CoreImageDownloadingOperationDelegate <NSObject>

@required
-(void) handleResponse:(UIImage*) response;
-(void) handleException:(MobileServiceException*) exception;

@end

@interface CoreImageDownloadingOperation : NSOperation{
}

-(id) initWithDelegate:(id<CoreImageDownloadingOperationDelegate>)del andImageID: (NSString *) image;

@property (nonatomic, assign) id <CoreImageDownloadingOperationDelegate> delegate;

When it's finish the downloading, calling the delegate method, to set the image to the imageView:

pragma mark - overridden from NSOperation
- (void) main {

    if (self.isCancelled)
        return;

    @autoreleasepool {

        @try {

            UIImage* image = [[CoreEnvironment sharedInstance] getImageFromServer: [imageID stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];

            if (self.isCancelled)
                return;

            if(delegate){
                [delegate handleResponse:image];
            }
            else
                NSLog(@"CachedImageView already deallocated");

        }@catch (NSException *exception) {

            TestLog(@"%@", exception.reason);

            if (self.isCancelled)
                return;

            if(delegate && [exception isKindOfClass:[MobileServiceException class]])
                [delegate handleException:(MobileServiceException*)exception];

        }
    }
}

The problem is: when I go to another page while the image is downloading, the cachedImageView is deallocated, but when the imageDownloadingOperation finishes downloading, the delegate is not nil, and it's trying to handleResponse... And of course I get message sent to deallocated...

I alloc init the operation like this in the CachedImageView:

CoreImageDownloadingOperation* imageDownloadingOperation = [[CoreImageDownloadingOperation alloc] initWithDelegate:self andImageID:imageKey];

enter image description here or:

enter image description here

-[CachedImageView isKindOfClass:]: message sent to deallocated instance 0x18868550
Was it helpful?

Solution 2

Where is your protocol declaration? I'd expect to see this:

@protocol CoreImageDownloadingOperationDelegate <NSObject> 

- (void) handleResponse:(UIImage *) image;

@end

@interface CoreImageDownloadingOperation : NSOperation{
}

-(id) initWithDelegate:(id<CoreImageDownloadingOperationDelegate>)del andImageID: (NSString *) image;

@property (nonatomic, assign) id <CoreImageDownloadingOperationDelegate> delegate;

You are getting the warning/crash because it can't find the responder handleResponse:

Also when invoking the delegate your better off doing:

if ([self.delegate respondsToSelector:@selector(handleResponse:)])
    [self.delegate handleResponse:image];

You don't need to check if (self.delegate && [self.delegate responds .... as it will return nil if the delegate is nil && if the selector is not implemented.

EDIT *

Where you create:

CoreImageDownloadingOperation* imageDownloadingOperation = [[CoreImageDownloadingOperation alloc] initWithDelegate:self andImageID:imageKey];

I suspect this is being released, turn this into a property of the class it's in. Then try again (make sure to release it when you're done though) i.e

In your .h

@property (nonatomic, retain) CoreImageDownloadingOperation* imageDownloadingOperation;

Then initialise with:

if (!self.imageDownloadingOperation)
    self.imageDownloadingOperation = [[CoreImageDownloadingOperation alloc] initWithDelegate:self andImageID:imageKey];

OTHER TIPS

The problem is: when I go to another page while the image is downloading, the cachedImageView is deallocated

The usual way to deal with this is to remove itself as a delegate in the dealloc of CachedImageView. Like

// in CachedImageView
- (void)dealloc {
  // CachedImageView keeps a reference to the operation
  // called imageDownloadingOperation
  imageDownloadingOperation.delegate = nil;
  [super dealloc];
}

A better way of writing the code is :

 if([self.delegate respondsToSelector:@selector(handleResponse:)){
    [self.delegate handleResponse:image];
}

This will avoid your crash.

I've solved the problem, I used CW0007007 solution and my own solution. So I turned my operation into a retained property:

@property (nonatomic, retain) CoreImageDownloadingOperation* imageDownloadingOperation;

after this I checked if the operation is still alive

if (!imageDownloadingOperation)
        imageDownloadingOperation = [[CoreImageDownloadingOperation alloc] initWithDelegate:self andImageID:imageKey];

then added to the operation queue.

In the dealloc set the delegate to nil ( ofc only if the operation is alive ), and release it:

if (imageDownloadingOperation) {
    imageDownloadingOperation.delegate = nil;
    [imageDownloadingOperation release];
}

in the operation: ( and now if the imageView deallocated, its delegate will be nil, and won't crash anytime )

if (delegate)        
    if ([delegate respondsToSelector:@selector(handleResponse:)])
        [delegate handleResponse:image];
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top