Question

I'm trying to do a simple reusable class which will fire up an action sheet to chose pic from library or camera and then crop the image and just return the final image to caller.

But when I tap on any button in UIActionSheet, its getting crashed showing EXC_BAD_ACCESS.

Here is the code: In my MainViewController.m

- (IBAction)ClickOnAdd:(id)sender{
    ImagePickerControl *pickerControl = [[ImagePickerControl alloc] initWithController:self Title:@"Chose Image" delegate:self cancelButtonTitle:@"Cancel" fromLibraryTitle:@"Library" fromCameraTitle:@"Camera"];
    [pickerControl showInView:self.view];  
}

Code in ImagePickerControl.h

@protocol ImagePickerControlDelegate;

@interface ImagePickerControl : NSObject<UIActionSheetDelegate,UIImagePickerControllerDelegate,UINavigationControllerDelegate>

@property (retain, nonatomic) UIActionSheet *menuSheet;
@property (assign, nonatomic) UIViewController *parentViewController;

- (id)initWithController:(UIViewController*) viewController Title:(NSString *)title delegate:(id<ImagePickerControlDelegate>)delegate cancelButtonTitle:(NSString *)cancelButtonTitle fromLibraryTitle:(NSString *)fromLibTitle fromCameraTitle:(NSString *)fromCamTitle;

-(void)showInView:(UIView*) parentView;
@end

@protocol ImagePickerControlDelegate <NSObject>


// Called when an image is picked. The view will be automatically dismissed after this call returns
- (void)imagePickerControlDidPickImage:(ImagePickerControl *)imagePickerControl pickedImage:(UIImage*)selectedImage;

// Called when we cancel the control's view
- (void)imagePickerControlDidCancel:(ImagePickerControl *)imagePickerControl;
@end

And finally in ImagePickerControl.m

- (id)initWithController:(UIViewController*) viewController Title:(NSString *)title delegate:(id<NTImagePickerControlDelegate>)delegate cancelButtonTitle:(NSString *)cancelButtonTitle fromLibraryTitle:(NSString *)fromLibTitle fromCameraTitle:(NSString *)fromCamTitle
{
    if(self = [super init]){
        self.menuSheet = [[UIActionSheet alloc] initWithTitle:title
                                                delegate:self cancelButtonTitle:cancelButtonTitle
                                                destructiveButtonTitle:nil
                                                otherButtonTitles:fromLibTitle,fromCamTitle,nil];

        self.parentViewController = viewController;


    }
    return self;
}

-(void)showInView:(UIView*) parentView
{
    [self.menuSheet showInView:parentView];
}

//This method was not called at all
-(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex{
}

After a quick research, from this SO post, I think, its clear that my control class gets released after i invoke. But in the answer there, it is mentioned to assign the controller as a strong property to mainviewcontroller. But I dont want to use this by creating a property and assigning. Just simply call it on add button click and get the callbacks, just like we use UIActionSheet or UIAlertView.

One more note is that, in my ClickOnAdd method, if I remove my custom class and add UIActionSheet itself there, then the callbacks are working fine. I'm expecting the same behaviour using my class. How that can be done?

Thanks in advance.

Was it helpful?

Solution 2

As already mentioned in the other answer, your ImagePickerControl is deallocated before the button is tapped. Therefore your app crashes.

You should assign the picker controller to a strong ivar in ClickOnAdd method. Then in your callback (I assume MainViewController gets a callback when the button is tapped) you should set this ivar to nil so that the picker controller is released.

It could work without an ivar if you weren't using ARC. In that case, you can keep the picker controller in the memory by not releasing it on ClickOnAdd and manually releasing it on the callback. However, that is just an invitation to memory leaks, therefore I would use a retained property even with non-ARC code.

UIActionSheet is a subclass of UIView therefore I assume it is added to the view hierarchy when you call showInView and therefore retained. It is probably released as soon as it is removed from the hierarchy so it is also good to keep a strong reference to it just in case it gets released sooner than you expected.

OTHER TIPS

The problem of your code is that your action sheet is deallocated by the time of clickButton at index.

You can easily get it by enabling Zombie Objects in Diagnostics tab (see attach).

In your case you should get error like

-[ImagePickerControl actionSheet:clickedButtonAtIndex:]: message sent to deallocated instance

So your pickerControl should be iVar in the initiating controller.

enter image description here

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