medvedNick
medvedNick

Reputation: 4510

Retain the delegate of UIImagePickerController

I've wrote a class which gets an image from the camera. Its header is as follows:

typedef void(^ImageTakenCallback)(UIImage *image);

@interface ImageGetter : NSObject <UIImagePickerControllerDelegate, UIPopoverControllerDelegate>
{
    UIImagePickerController *picker;
    ImageTakenCallback completionBlock
}

-(void) requestImageInView:(UIView*)view withCompletionBlock:(void(^)(UIImage*))completion;

@end

As you can see, I'm trying to make something like that in client code:

[[[ImageGetter alloc] init] requestImageInView:_viewController.view withCompletionBlock:^(UIImage *image) {
    // do stuff with taken image
}];

Here is how I've implemented ImageGetter:

-(void) requestImageInView:(UIView*)view withCompletionBlock:(ImageTakenCallback)completion
{
    completionBlock = [completion copy];

    picker = [[UIImagePickerController alloc] init];
    picker.sourceType = UIImagePickerControllerSourceTypeCamera;
    picker.delegate = self;
    [view addSubview:picker.view];
}

- (void)imagePickerController:(UIImagePickerController *)picker_
        didFinishPickingImage:(UIImage *)image
                  editingInfo:(NSDictionary *)editingInfo
{
    [picker.view removeFromSuperview];
    picker = nil;

    completionBlock(image);
}

The problem is since I'm using ARC, the instance of ImageGetter is deallocated instantly after call for -requestImage..., so the weak delegate of picker becomes nil.

Which are common ways to resolve such a issue?

I can see some ways, however, none of them seems to be quite right:

  1. retain ImageGetter from client code, for example, assign it to a strong property. The problems here are: I wont be able to release it by setting this property to nil right after I get image, because this will mean setting retain count of object to 0 while executing the method of this object. Also, I don't want unnecessary properties (well, it is not a big problem, but nevertheless).
  2. disable ARC for ImageGetter and manually retain at start itself and release after sending image to callback.
  3. make static manager ImageGetterManager, which will have method requestImage..., it will create ImageGetter instances, retain them, redirect the requestImage... call, get callbacks from them and release. That seems the most consistent way, but is not it a bit complex for such a little code?

So how can I build such a class?

Upvotes: 1

Views: 568

Answers (3)

bsarr007
bsarr007

Reputation: 1964

You can use the following strategy:

ImageGetter* imgGetter = [[ImageGetter alloc] init];
[imgGetter requestImageInView:_viewController.view withCompletionBlock:^(UIImage *image) {
    // do stuff with taken image
    [imgGetter releaseCompletionBlock]; // With this line, the completion block will retain automatically imgGetter, which will be released after the release of the completionBlock.
}];

Inside your ImageGetter implementation class, create a method that you can call inside the block like this.

-(void) releaseCompletionBlock
{
  completionBlock = nil;
}

Upvotes: 0

Martin R
Martin R

Reputation: 539745

You can handle that within the ImageGetter class by creating and releasing a "self-reference". In a class extension in the implementation file, declare a property

@interface ImageGetter ()
@property (strong, nonatomic) id selfRef;
@end

In requestImageInView:, set self.selfRef = self to prevent deallocation.

In the completion method, set self.selfRef = nil.


Remark: Actually you can manage the retain count even with ARC:

CFRetain((__bridge CFTypeRef)(self));   // Increases the retain count to prevent deallocation.
CFRelease((__bridge CFTypeRef)(self));  // Decreases the retain count.

But I am not sure if this is considered "good programming" with ARC or not. Any feedback is welcome!

Upvotes: 1

Leijonien
Leijonien

Reputation: 1413

If this issue is introduced when switching to ARC, I should just go for option 1, and define it as a strong property.

However the behaviour is a bit different than you described for option 1: Setting the property to nil, does NOT mean the object is instantly released, it will just cause a decrement of the retaincount. ARC will handle that fine, the object will be released as soon as all referenced objects have 'released' it.

Upvotes: 0

Related Questions