Reputation: 4272
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];
or:
-[CachedImageView isKindOfClass:]: message sent to deallocated instance 0x18868550
Upvotes: 0
Views: 709
Reputation: 4272
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];
Upvotes: 0
Reputation: 122439
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];
}
Upvotes: 1
Reputation: 5681
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];
Upvotes: 0
Reputation: 2918
A better way of writing the code is :
if([self.delegate respondsToSelector:@selector(handleResponse:)){
[self.delegate handleResponse:image];
}
This will avoid your crash.
Upvotes: 0