When writing code, there are many situations that must be treated as runtime errors : an alloc/init returns nil, a resource is not found, a [someClass canDoThis] returns NO for an absolutely-needed feature where YES would be the natural answer, ...
For all these situations, I have written an exitWithMessage routine (that displays an alert box), and each class has a kill method that frees allocated memory.
So... When in an init method, you have these kind of exceptions, I supposed you could do :
[self kill];
[OneClass exitWithFatalErrorMessage];
return nil;
- (void) exitWithFatalErrorMessage:(NSString*)message
{
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:NSLocalizedStringFromTable(#"Error" #"ErrorMessages", #"") message:message delegate:self cancelButtonTitle:NSLocalizedStringFromTable(#"Understood", #"ErrorMessages", #"") otherButtonTitles: nil];
[alert show];
[alert release];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
// stop the normal running of the app, there is a situation that would prevent it
}
- (void)kill
{
self.member = nil;
self.member2 = nil;
...
}
But this does not work... My alert is not displayed (the exitWithMessage works fine when used anywhere else than into an init method.
How would you handle those cases ? Is this piece of coding a fine way to do ?
If yes, why does my alert do not display (I'm into a view controller initWithCoder method for the example) ?
Are you actually calling the exitWithFatalErrorMessage method, because in your code you call exitWithMessage instead, try changing it to this:
[OneClass exitWithFatalErrorMessage:#"Message"];
Related
I create a class to call UIAlertview show on my screen. I write the UIAlert function in another class. Both these two classes are not my viewController class.
I use this UIAlert, which is a UITextfield inside, to store texts into a plist file.
here is the class to call UIAlert:
#import "Story.h"
#implementation Story
...
+ (void)stage1
{
AlertClass *pointer = [AlertClass new];
[pointer doAlert];
}
here is the class AlertClass.m file:
- (void)doAlert
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Title" message:#"Message" delegate:self cancelButtonTitle:#"Done" otherButtonTitles:nil];
alert.alertViewStyle = UIAlertViewStylePlainTextInput;
[alert show];
}
//this makes crash!
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
self.storyFlow.text = [alertView textFieldAtIndex:0].text;
}
Before I add UIAlertViewDelegate in the .h and override the method "clickedButtonAtIndex", it works great. However, I need to store some data from the UITextfield inside the alert view. I get crash and don't know the message it responds as following.
Please help me to solve this problem. Thanks.
[crash pic] https://dl.dropbox.com/u/47381923/crash.tiff
do an NSLog on the text you get back from the Alert View to see whether that is the crash or the subsequent 'self.storyFlow.text = ' is causing it. Perhaps self.storyFlow has not been created yet (with alloc/init)
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.
would you please look at that piece of code:
/* This app is a game, the user can click an "abort" button anytime,
* and he/she is therefore asked for confirmation ("really abort game?")
*/
- (IBAction)btnAbortClicked:(id)sender {
UIAlertView *alert = [[UIAlertView alloc] init];
[alert setMessage:#"Really abort game?"];
[alert setDelegate:self];
[alert addButtonWithTitle:#"Yes"];
[alert addButtonWithTitle:#"No"];
[alert show];
[alert release];
}
/* Delegate method (I don't like it, I wish I had modal blocking windows) */
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if (buttonIndex == 0)
[self quitGame];
}
/* pop the view controller */
- (void)quitGame {
[self.navigationController popToRootViewControllerAnimated:YES];
}
The problem is simple - but apparently not enough for me to solve. The UIViewController gets popped, but doesn't get deallocated. And the problem is strictly related to the UIAlertView, because if I just call quitGame from btnAbortClicked:, the view controller is popped and immediately deallocated.
Instead, it seems some mysterious entity retains it.
Can you help me? Thanks in advance.
Well, I think that you're still inside the alertView when clickedButtonAtIndex is called. I'd suggest moving to alertView:disDismissWithButtonIndex instead, so that you're called after the alertview disappears.
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
Here is the code I have:
Phone SDK undestanding cocoa object live cycle:
- (void) DismissWelcomeMessage: (UIAlertView *) view
{
[view dismissWithClickedButtonIndex:0 animated:YES];
}
- (void) ShowWelcomeMessage
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Blah" message:#"Blah Blah" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
[self performSelector:#selector (DismissWelcomeMessage:) withObject: alert afterDelay: WELCOME_MESSAGE_DELAY];
[alert release];
}
ShowWelcomeMessage is called first.
Why DissmissWelcomeMessage works fine and does not crash even though alert object is released?
Is because Dismiss function uses copy of the object passed on stack as a parameter when function? But even then would not it be just a copy of the pointer pointed to the now deallocated object?
Or [alert release] just decriment reference counting and does not really do the same as
delete in C++?
performSelector retains the object, thus your release doesn't cause its retain count to go to zero.
See NSObject docs
This method retains the receiver and the anArgument parameter until after the selector is performed.
It's possible that performSelector is retaining the object passed in, which is why it's still valid when DismissWelcomeMessage is called.