EXEC_BAD_ACCESS on a UIActionSheet with ARC - iphone

Just converted a project to ARC and am now getting a EXEC_BAD_ACCESS after I dismiss a UIActionsheet, it was previously working and I am unsure if this is even ARC related. Zombies is enabled but showing me nothing and I tried instuments and it also gave me nothing.
This is presented in a modal view controller, case 0, the quit button works fine but the other two give me the bad access error.
This is my first conversion to ARC, am I missing something here?
Action sheet Creation:
-(IBAction)quitPressed:(id)sender {
UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:#"Quit This Game?" delegate:self cancelButtonTitle:#"Keep Playing" destructiveButtonTitle:#"Quit" otherButtonTitles:#"Quit and Reveal Answers",nil];
[sheet showInView:self.view];
}
Action sheet delegate:
-(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex{
switch (buttonIndex) {
case 0: //quit
[self dismissViewControllerAnimated:NO completion:^{
[self.delegate quitGame];
}];
break;
case 1: //quit and reveal
NSLog(#"reveal");
break;
case 2: //cancel
NSLog(#"cancel");
break;
default:
break;
}
}

If your delegate is declared strong in the .h file. Have you initialized the self.delegate at least once in the .m file(preferably viewDidLoad) using
self.delegate = [[UIApplication sharedApplication] delegate];

Delegates should be weak or assign (__weak / __unsafe_unretained for ivars) to avoid any retain cycles.
Hold a reference to the sheet that you create. You can clear that reference once the sheet was closed.

Thanks everyone for the help. I found the problem when I ran the project under xcode 4.5. It gave a compile error: switch case is protected in scope
I wasn't getting that error in xcode 4.3
It was solved in this thread When converting a project to use ARC what does "switch case is in protected scope" mean?
I wrapped each case in curly brackets and the problem has been fixed.

Related

ABPeoplePickerNavigationController crashes while being dismissed in Simulator only

I get this crash in the simulator only. On a real device it works and debugs fine.
(Therefore it is not too critical but I am working on a presentation for which the simulator comes handy.)
- (BOOL)peoplePickerNavigationController:
(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person {
[self displayPerson:person];
if ([self respondsToSelector:#selector(dismissViewControllerAnimated:completion:)]){
[self dismissViewControllerAnimated:YES completion:nil];
} else {
[self dismissModalViewControllerAnimated:YES];
}
return NO;
}
The crash is on:
[self dismissViewControllerAnimated:YES completion:nil];
All I see is: "Thread 1: EXC_BREAKPOINT (code=EXC_I386_BTP, subcode=0x0)"
There is no specific output in the debug window. Zoombies is enabled. I do ARC. I am using storyboard but I call the ABPersonPicker... programmatically using the following code:
-(void)showPeoplePickerController
{
ABPeoplePickerNavigationController *picker = [[ABPeoplePickerNavigationController alloc] init];
picker.peoplePickerDelegate = self;
if ([self respondsToSelector:#selector(presentViewController:animated:completion:)]) {
[self presentViewController:picker animated:YES completion:nil];
} else {
[self presentModalViewController:picker animated:YES];
}
return;
}
Remember that it is running well on the devices that I have (iPod Touch 4th gen, iPhone 4 and iPhone 4S)
Your problem is that you don't keep a strong reference to the picker. Add a ivar to hold it, then when done in the delegate callback, use a dispatch asyn block on the main queue to nil the ivar out and release it. (Sorry no code entering this from an iPad)
EDIT: look in your showPeoplePickerController method - you create the picker and save it in an automatic which gets released when you exit the method. You want to keep a strong reference to the picker by using an ivar instead of the automatic.
Then you don't want to release the picker in a delegate callback - this often works but will bite you at unexpected times, so you want to release this type of object after the delegate callback is done. In fact I usually set the objects delegate property to nil first if I can then release it with 'picker = nil' . In a block 'picker' gets turned into 'self->picker'. You can also use performSelector:afterDelay:0 too I just prefer blocks.
EDIT2: So what I do in
- (BOOL)peoplePickerNavigationController:shouldContinueAfterSelectingPerson:
is just return NO. I wait until this message:
- (void)peoplePickerNavigationControllerDidCancel:
to dismis it. I also have some other code that pops a an action sheet that confirms saving a selected address and also does a dismiss, but its old code and not sure how it works now. In any case hope this helps.

Changing UISwitch to On

I have a UISwitch object in my .xib file. Its state is On by default. If the user switches it to Off, I want to ask if he's sure, and if he clicks No - automatically switch it back to On.
So I set these outlet and action in my .h file:
#property (weak, nonatomic) IBOutlet UISwitch *campaignSwitch;
- (IBAction)checkCampaignSwitch:(id)sender;
This is the checkCampaignSwitch method:
- (IBAction)checkCampaignSwitch:(id)sender {
if(self.campaignSwitch.isOn==FALSE)
{
[self allertMessage:#"Receive updates" :#"Are you sure you don't want
to receive updates?" :#"No" :#"Yes"];
}
}
This calls for method to display an Alert message with various parameters (title, text, cancel button and other button).
I also registered for UIAlertViewDelegate and in my .m file I'm trying to implement the clickedButtonAtIndex method like this:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
switch (buttonIndex) {
case 0:
[self.campaignSwitch setOn:TRUE];
break;
default:
break;
}
}
I'm getting the Alert message, but nothing happens when I click the No button. Nor the Yes.
So how do I switch it back On?
Make sure you have all connections connected in Interface Builder. Also, make sure your switch statement in your alert view is associated with the right button (i.e. make sure you getting to case 0).
To turn a switch off, see the code below.
// Set the switch on
[_switch setOn:YES];
// Set the switch off
[_switch SetOn:NO];
I usually do it like this in order for it to be drawn immediately:
[emailAlertSwitch setOn:NO animated:YES];
OR
[emailAlertSwitch setOn:YES animated:YES];
Is it entering the alertView delegate callback?
You need to use the setOn: animated: method
[self.campaignSwitch setOn:YES animated:YES];
Simply using setOn without animated will not show an animation.
Had to make sure the UIAlertView is delegated:
UIAlertView *displayMessage = [[UIAlertView alloc]
initWithTitle:messageTitle
message:alertMessage
**delegate:self**
cancelButtonTitle:cancelButton
otherButtonTitles:otherButton,
nil];

Crash when UIAlertView is clicked

When I click the UIAletView, I receive the following error.
alertView:clickedButtonAtIndex:]: message sent to deallocated instance 0x84c7010
This is the code I have used.
UIAlertView *testAlert = [[ UIAlertView alloc]initWithTitle:messageTitle message:messageBody delegate:self cancelButtonTitle:messageClose otherButtonTitles:messageTryAgain, nil];
testAlert.tag = 2;
[testAlert show];
[testAlert release];
And I have the delegate method
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
}
When I click the UIAlertView , even before the control reaches the delegate method, the app crashes. What could be the reason. What am I doing wrong?
This is "one hack of a solution".
Hopefully it helps you understand that your delegate is the memory issue. The delegete (in this case self) is deallocated somehow before the testAlert is dismissed
// retain self to avoid crash you were experiencing earlier
UIAlertView *testAlert = [[ UIAlertView alloc]initWithTitle:messageTitle message:messageBody delegate:[self retain] cancelButtonTitle:messageClose otherButtonTitles:messageTryAgain, nil];
testAlert.tag = 2;
[testAlert show];
[testAlert release];
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
// release self because you've gotten past the crash
[self release];
}
This is in no way an elegant solution and should encourage you to debug your app further to find out why self is being deallocated prematurely
Just wondering, could you show us your .h file?
If I had to hazard a guess, you've forgotten to set your class to respond to UIAlertViews as a delegate
You might be missing something like this:
#interface MyClass : UIViewController <UIAlertViewDelegate>
If ARC enable UIAlertView Object retain and no need to release, it's automatically release your object.

unable to detect crash in EKEventEditViewController

when i click on done button at EKEventEditViewController then app gets crashed saying "exc_bad_access".I also used break point to detect it but cannot find it. There is nothing about crash in gdb. Where should i check crash. Done-button do not shift control to EKEventEditViewDelegate - method. It just crash.Help me out plz.
here is my code
-(void)viewWillAppear:(BOOL)animated
{
EKEventEditViewController *addController = [[EKEventEditViewController alloc]
initWithNibName:nil bundle:nil];
addController.eventStore = self.eventStore;
addController.event = event;
addController.editViewDelegate = self;
[self presentModalViewController:addController animated:YES];
[super viewWillAppear:YES];
}
#pragma mark -
#pragma mark EKEventEditViewDelegate
- (void)eventEditViewController:(EKEventEditViewController *)controller
didCompleteWithAction:(EKEventEditViewAction)action {
NSError *error = nil;
EKEvent *thisEvent = controller.event;
switch (action) {
case EKEventEditViewActionCanceled:
break;
case EKEventEditViewActionSaved:
[controller.eventStore saveEvent:controller.event
span:EKSpanThisEvent error:&error];
break;
case EKEventEditViewActionDeleted:
[controller.eventStore removeEvent:thisEvent span:EKSpanThisEvent
error:&error];
break;
default:
break;
}
[controller dismissModalViewControllerAnimated:YES];
[self backTopreviousController];
}
-(void)backTopreviousController
{
[self.navigationController popToRootViewControllerAnimated:YES];
}
exc_bad_access suggests that you're accessing memory that has been deallocated (probably a mememory management problem). These issues are a bit hard to tackle because you might release an object (which you should not) at some point and you only run into a problem a little later when accessing this object.
You can try the following:
Click the "Run Button Dropdown"
From the list choose Profile
The program "Instruments" should open where you can also choose Zombies
Now you can interact with your app and try to cause the error
As soon as the error happens you should get a hint on when your object was released and therefore deallocated.
(source: dimzzy.com)
Debug the program after setting a breakpoint at the beginning of eventEditViewController:didCompleteWithAction. Once your program reaches the breakpoint execute step by step.
Doing like this, you will know either:
which statement makes the program crash, or
that the program crashes before even entering that method.
In case 1, you should inspect each object you send a message to and make sure it has not been deallocated. In case 2, you should inspect the action definition that is associated to the Done button.
Your code is crashing because of the following lines.
[controller dismissModalViewControllerAnimated:YES];
[self backTopreviousController];
either you want to pop or dismiss. But not both.
It depends how you have called this class. Push or presentModal !

UIAlertView exits EXC_BAD_ACCESS error

I have such error: when I click navigationbar.backItemButton I'm showing UIAlertView with two buttons. When I press on any of them application terminates just with EXC_BAD_ACCESS. Method - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex don't called. How can I solve it? Thanx!
//h - file
#interface DetailsTableViewController : UITableViewController <UITextFieldDelegate, UIAlertViewDelegate>
//m - file
- (void)viewWillDisappear:(BOOL)animated
{
//if changes unsaved - alert reask window
if (isDirty)
{
UIAlertView *message = [[UIAlertView alloc] initWithTitle:#"Save changes?"
message:#"Press YES if you want to save changes before exit, NO - other case."
delegate: self
cancelButtonTitle: #"NO"
otherButtonTitles: #"YES", nil];
[message show];
[message autorelease];
}
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
NSString *title = [alertView buttonTitleAtIndex: buttonIndex];
if([title isEqualToString: #"YES"])
{
[self saveBtnUserClick];
}
}
I think the problem is that after you tapped back button your current controller is removed from navigation stack and deallocated, so when alert tries to call its delegate methods it calls them on deallocated object which results in EXC_BAD_ACCESS error. To workaround the problem I see 2 obvious options (although there may be better solutions):
Extra retain your controller somewhere (in previous controller may be), but you need to find way to release it when you're done.
Create your custom button instead of standard "back" and just show alert when it tapped. Then in alert's delegate method pop your current controller from navigation stack.
Try Changing delegate to nil instead of self. It fixed my issue.
Is your view controller implementing the UIAlertViewDelegate? If not, add in you interface declaration before the { starts.
Also try NSLogging inside the clickedButtonAtIndex method and print the buttonIndex values and see the console.
Edit: Reading your post again, I guess you indeed have missed the UIAlertViewDelegate in your interface declaration.
Probably [message autorelease];
is you mistake use
[message release];
Because you have used [[UIAlertView alloc] init.....]; there for you should release the memory.
autorelease is something will work with the structure which memory is compiler dependent or you have not given the memory manually.
Enjoy.
"Try Changing delegate to nil instead of self. It fixed my issue." worked for me. Thanx