UIAlertView fails to show and results in “EXC_BAD_ACCESS” error - iphone

A method is called when a return button on the keyboard is pressed. After calling another method which returns an integer a message is created based on that integer. The message is then passed into an UIAlterView and displayed to the user. The alert doesn't have any options (hence why I'm not calling a delegate), but simply notifies the user of what happened.
Edit: Below is the full method (previously displayed partial). When I comment out everything before the UIAlertView and substitute the string #"test" instead of passing message the Alert is shown successfully. Am I not handling memory correctly with my structure?
- (IBAction)joinButton {
struct userInfo localUser;
[emailAddress resignFirstResponder];
//convert textField text to char array in structure
localUser.firstName = [self convertStringtoCharArray:firstName.text];
localUser.lastName = [self convertStringtoCharArray:lastName.text];
localUser.username = [self convertStringtoCharArray:username.text];
localUser.email = [self convertStringtoCharArray:emailAddress.text];
localUser.ipAddress = [self convertStringtoCharArray:localIPAddress.text];
localUser.latitude = currentLocation.coordinate.latitude;
localUser.longitude = currentLocation.coordinate.longitude;
//pass structure to be sent over socket
int result = [myNetworkConnection registerWithServer:&localUser];
NSString *message = nil;
//process result of sending attempt
if (result == 0) {
//registration succesful
message = [NSString stringWithString:#"Registration successful"];
} else if (result == 1) {
//server unavailable
message = [NSString stringWithString:#"Server unavailable. Please check your wi-fi settings and try again."];
} else if (result == 2) {
//unable to establish connection
message = [NSString stringWithString:#"Unable to communicate with server. Please check your wi-fi settings and try again."];
} else if (result == 3) {
//username already in use
message = [NSString stringWithString:#"Username in use. Try another username."];
}
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Registration"
message:message
delegate:nil
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[alert show];
[alert release];
}
When I execute the code the iPhone greys out like it is about to display an alert but crashes. I get a EXC_BAD_ACCESS error in the console. Am I not releasing either the alert or the message correctly? Here is the console output:
Program received signal: “EXC_BAD_ACCESS”.
(gdb) backtrace
#0 0x30011944 in objc_msgSend ()
#1 0x3054803e in NSPopAutoreleasePool ()
#2 0x3054c808 in -[NSAutoreleasePool release] ()
#3 0x30936ac4 in _UIApplicationHandleEvent ()
#4 0x3204696c in PurpleEventCallback ()
#5 0x30254a76 in CFRunLoopRunSpecific ()
#6 0x3025416a in CFRunLoopRunInMode ()
#7 0x320452a4 in GSEventRunModal ()
#8 0x308f037c in -[UIApplication _run] ()
#9 0x308eea94 in UIApplicationMain ()
#10 0x000020bc in main (argc=1, argv=0x2ffff508) at /Users/reu2009/Documents/iPhone Development/Development/BuddyTracker/main.m:14
(gdb) frame 10
#10 0x000020bc in main (argc=1, argv=0x2ffff508) at /Users/reu2009/Documents/iPhone Development/Development/BuddyTracker/main.m:14 14 int retVal = UIApplicationMain(argc, argv, nil, nil);
Edit: removed [message release]; and assigned strings using [NSString stringWithString]; based on answers.

i had an issue like this ...i was calling uiAlertView from a background thread ....call it from the main thread

Objects returned from convenience constructors are already set to autorelease. While you declared a pointer to "message", the "message" object itself doesn't belong to you, since you used the #"string" convenience constructor to create the NSString object. Thus, you don't need to release it.
When you release it manually, it then gets released too many times (once manually, and once when the autorelease process rolls around) and throws the error.
Here's some additional information from Apple:
http://developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmPractical.html
Good rule of thumb: unless you use one of the alloc or init or copy methods to create an object (or if you retain the object yourself) you don't need to release it, but can rely on the method that actually created it to do that work for you.

Try it with NSZombieEnabled = YES.
Go into the Info of you Executable.
Click on the Arguments tab.
Click + on "Variables to be set in the environment."
Type NSZombieEnable and YES.
When the memory is released that has already been released, NSZombie will display the address, then you can use Instruments to find the actual object. Corbin's Treehouse has a good overview of how to do this:
Instruments on Leopard: How to debug those random crashes in your Cocoa app

Sean is right - you don't need to call [message release] here, because you're never actually retaining the message object.
Instead of just saying message = #"string", you need to say message = [NSString stringWithString:#"string"]; To be completely honest I'm not sure why (maybe someone can comment and I can improve this post!) but that should do the trick.

I had the same problem here with a UIAlertView, in my case, I had another class implementing the alert, and, from another one I was calling a static method. Like the following:
ClassA
...
doSomething {
... some stuff ...
[MyAlertView showAlert];
... some other stuff...
}
What I suspect is that, as the alertview is shown asynchronously when I clicked the button the object was already released.
To verify that, I changed the code to instantiate the alert and not release it. And everythong worked.
My final solution was to declare a variable in the parent view, and deallocate it with the other variables when the view is deallocated.

This could case due to updating UIKit from background thread
I solved it like this
UIAlertView *alertMSG = [[UIAlertView alloc] initWithTitle:nil
message:#"Your mnessage here"
delegate:self
cancelButtonTitle:#"Title here"
otherButtonTitles: nil];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[alertMSG show];
}];

Related

memory crash when i try to stopAnimating UIActivityIndicatorView

When ever i am trying to update the UIActivityIndicatorView from thread . the app is getting crashed by throwing an exception
modifying layer that is being finalized - 0x7e177fd0
-[CALayer removeAnimationForKey:]: message sent to deallocated instance 0x7e177fd0 .
when i try track the memory leaks form the mallocDebugger tool .
this crash is not happening at all the time happening 1 out of 10
please help me out rom this memory issue
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
[autoRechargeCell addSubview:activityIndicator];
[self.activityIndicator startAnimating];
if( [PennyTalkAPI getBalanceInfoForAccount:appDelegate.accountNumber withPIN:appDelegate.pinNumber])
{
[autoRechargeCell.switchField setOn:[[NSUserDefaults standardUserDefaults] boolForKey:#"AutoRecharge"]];
[self.activityIndicator stopAnimating]; <<<<<<<<<<<<<<<<<<<<<<
}
else
{
[self.activityIndicator stopAnimating];
}
[pool release];
This is the code i have written
Without looking at code and seeing error I assume you release your Activity Indicator and then you are trying to access it to animate..
Solution: Declare UIActivityIndicator object in .h file synthesize and release it in -(void)dealloc method.
This is either a double release error, or a dangling pointer. You should enable zombie detection in your Scheme's configuration, and try the Zombies instrument in Instruments.

Why doesn't the UIAlertView work?

I am testing this piece of code but when I run it, it does not trigger the UIAlertView. When the code hits If (ongoingGame = YES) and NSLog it jumps directly to the 'otherButtonTitles:nil' without executing the UIAlertView.
Can someone please explain to me why it does not trigger it?
-(IBAction)continueGame_button:(id)sender {
//=====CHECK IF THERE IS AN ON-GOING GAME, IF SO CONTINUE=====//
AccessCurrentGameData *isThereAnOngoingGameFunction = [AccessCurrentGameData new];
BOOL ongoingGame = [isThereAnOngoingGameFunction checkIfGameOngoing];
[isThereAnOngoingGameFunction release];
NSLog(#"+ + +continueGame_button+ + +");
NSLog(#"ongoingGame = %#\n", (ongoingGame ? #"YES" : #"NO"));
if (ongoingGame == YES) {
NSLog(#"++++++++++++++++++");
NSLog(#"++++++++++++++++++");
NSLog(#"++++++++++++++++++");
NSLog(#"++++++++++++++++++");
NSLog(#"++++++++++++++++++");
//
UIAlertView *continueGame = [[UIAlertView alloc] initWithTitle:#"Fortsätta spel"
message:#"Det finns ett aktivt spel, klicka Spela eller Tillbaka"
delegate:self
cancelButtonTitle:#"Tillbaka"
otherButtonTitles:nil];
[continueGame show];
[continueGame release];
}
exit(0);
}
You are assigning onGoingGame to YES, not comparing it to YES. Use == instead of =.
Your alert code is just fine I use that form (three lines - init, show, release) all of the time to do alerts.
I suggest that the exit(0) is the root of the problem. If you want to exit after the user closes the alert, you should assign a delegate which will close the app when the user taps on the close button. Use your code, but remove the exit(0). Then implement the UIAlertViewDelegate as follows:
-(IBAction)continueGame_button:(id)sender {
//=====CHECK IF THERE IS AN ON-GOING GAME, IF SO CONTINUE=====//
AccessCurrentGameData *isThereAnOngoingGameFunction = [AccessCurrentGameData new];
BOOL ongoingGame = [isThereAnOngoingGameFunction checkIfGameOngoing];
[isThereAnOngoingGameFunction release];
NSLog(#"+ + +continueGame_button+ + +");
NSLog(#"ongoingGame = %#\n", (ongoingGame ? #"YES" : #"NO"));
if (ongoingGame == YES) {
NSLog(#"+++++++++ ONGOING GAME +++++++++");
//
UIAlertView *continueGame = [[UIAlertView alloc] initWithTitle:#"Fortsätta spel"
message:#"Det finns ett aktivt spel, klicka Spela eller Tillbaka"
delegate:self
cancelButtonTitle:#"Tillbaka"
otherButtonTitles:nil];
[continueGame show];
[continueGame release];
}
}
- (void) alertViewCancel:(UIAlertView *)alertView{
//If you have other alerts, you may want to check the title of the alert to
//make sure that you only exit when THIS alert is dismissed
exit(0);
}
Dont' forget to add the <UIAlertViewDelegate> code to your header (.h) file.
You can also use - (void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex, if you want multiple buttons, with one of them being a specific "Quit" button.
Please note that Apple discourages using exit() in apps that are released to the App Store, and using it might get your app rejected.
You can try this line instead.
[[[[UIAlertView alloc] initWithTitle:#"this is my message" message:nil delegate:nil cancelButtonTitle:#"Dismiss" otherButtonTitles:nil] autorelease] show];
Also, I believe Apple does not advice using exit() within your application. They always want the user to use the "Home" button to exit an app. The exit() call is a hard exit and this might be the reason you are not seeing the Alert.
You should not release it immediately. And you exit the app even before the alert view gets a chance to display itself. :)
Your code will continue to run even when the alert view is visible.
Fixage
Remove the exit call
Don't release the alertview. Release it in it's owner's dealloc method.
Make the alert view an instance variable and add a retain property to it.
Initialize the alertview in its getter if it's not yet available.
Set it's attributes in the IBAction and show it.
Add the appropriate delegate methods.
If I wasn't writing this answer on an iPod touch I'd post some example code. You can find lots of such code with Google.
Also, if your app isn't English-only you should always use localization provided by Foundation. Otherwise you can get English text with default error messages and other UI elements.

Invoke model method with block that will run on the main thread

one of the central tenets of the architecture of my latest app is that I'm going to call methods on the app's model which will be async and accept failure and success scenario blocks.
i.e., The UI calls the model method with 2 blocks, one for success and one for failure.
This is great because the context of the original call is retained, however, the block itself is called on the background thread. Is there anyway of calling a block on the main thread??
Hopefully I have explianed it ok, if not, basically, my model methods are async, return immediately and create a new thread on which to run the op. Once the op returns I will invoke a block which will postprocess the returned data, THEN i need to call the block for the success scenario defined by the called inside the UI. However, the success and failure scenario blocks defined in the UI should be called in the main thread because I need to interact with UI elements which should only be done on the main thread I believe.
many thanks
Something like this is probably what you're after:
- (void) doSomethingWhichTakesAgesWithArg: (id) theArg
resultHandler: (void (^)(BOOL, id, NSError *)) handler
{
// run in the background, on the default priority queue
dispatch_async( dispatch_get_global_queue(0, 0), ^{
id someVar = [theArg computeSomething];
NSError * anError = nil;
[someVar transmuteSomehowUsing: self error: &anError];
// call the result handler block on the main queue (i.e. main thread)
dispatch_async( dispatch_get_main_queue(), ^{
// running synchronously on the main thread now -- call the handler
handler( (error == nil), theArg, anError );
});
});
}
If you are using GCD, you can use the "get main queue":
dispatch_queue_t dispatch_get_main_queue()
Call this inside an async dispatch. i.e.
dispatch_async(dispatch_get_main_queue(), ^{
/* Do somthing here with UIKit here */
})
The example block above could be running in an async background queue and the example code would send the UI work off to the main thread.
Similar approach works also with NSOperationQueue:
NSBlockOperation *aOperation = [NSBlockOperation blockOperationWithBlock:^
{
if ( status == FAILURE )
{
// Show alert -> make sure it runs on the main thread
[[NSOperationQueue mainQueue] addOperationWithBlock:^
{
UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:#"Alert" message:#"Your action failed!" delegate:nil
cancelButtonTitle:#"Ok" otherButtonTitles:nil] autorelease];
[alert show];
}];
}
}];
// myAsyncOperationQueue is created somewhere else
[myAsyncOperationQueue addOperation:aOperation];
NSObject has a method:
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait
Create a method that takes a NSDictionary parameter in a convenient class that will always be around (like your app delegate, or a singleton object), package up the block and its parameters into a NSDictionary or NSArray, and call
[target performSelectorOnMainThread:#selector(doItSelector) withObject:blockAndParameters waitUntilDone:waitOrNot];

Cocoa-Touch: performSelectorOnMainThread: weird behavior + crash

I have a situation where I'm lazy loading images from the www.
It's a list of items, when one item is tapped, a detail view is pushed to a nav controller.
In that detail view the item has an image, which first is a default image, and I want to start loading it's image from a URL.
So what I do is create an object which once initialized detaches a new thread which in turn loads the content and afterwards notifies my view that the data is loaded:
// in MyLoader:
- (MyLoader *)initWithUrl:(NSURL *)url requester:(id)requester {
self.url = url;
self.requester = requester; // both are nonatomic, retain properties
[self performSelectorInBackground:#selector(loadIt) withObject:nil];
}
- (void)loadIt {
NSAutoreleasePool *arp = [[NSAutoreleasePool alloc] init];
NSData *data = [NSData dataWithContentsOfURL:url];
[requester performSelectorOnMainThread:#selector(dataReady) withObject:data waitUntilDone:YES;
[arp release];
}
// in MyRequester:
- (void)somewhere {
MyLoader *loader = [[[MyLoader] alloc] initWithUrl:someUrl requester:self] autorelease];
// then I retain loader somewhere, it's more complicated but I have verified that it's properly retained.
}
A few notes:
First I thought there might be a problem with some of the variables. I put a breakpoint right before performSelectorOnMainThread and confirmed that data and requester were both OK.
Then I thought it was caused by passing the NSData across the threads, so I changed withObject:nil. It still crashes.
When I further investigated, the crash was very strange. I specified waitUntilDone:YES, I've placed a breakpoint in the requester's dataReady. But the performSelectorOnMainThread call returns (it reaches the breakpoint after it) while not reaching the breakpoint inside dataReady. BTW, - (void)dataReady:(NSData*)'s body for now only contains int x = 1; (to place a breakpoint on). Also, I've tried setting waitUntilDone:NO, it still crashes.
The selector isn't performed (the breakpoint is not reached), but the crash happens a short while after the call.
Does anyone have any idea what's wrong?
This is obvious, but just to be clear, if I just comment out the [requester performSelectorOnMainThread... part, it doesn't crash.
Also, here's a stack trace, but it's not helpful at all.
#0 0x00a71004 in ___TERMINATING_DUE_TO_UNCAUGHT_EXCEPTION___ ()
#1 0x93436e3b in objc_exception_throw ()
#2 0x0028aca6 in __NSThreadPerformPerform ()
#3 0x00a098e1 in CFRunLoopRunSpecific ()
#4 0x00a08c48 in CFRunLoopRunInMode ()
#5 0x0005a78d in GSEventRunModal ()
#6 0x0005a852 in GSEventRun ()
#7 0x0168a003 in UIApplicationMain ()
#8 0x000028d4 in main (argc=1, argv=0xbffff100) at /Users/myName/Document/appName/main.m:14
You have:
[requester performSelectorOnMainThread:#selector(dataReady) withObject:data waitUntilDone:YES;
should be:
[requester performSelectorOnMainThread:#selector(dataReady:) withObject:data waitUntilDone:YES;
notice: #selector(dataReady:) (with colon)
Since you're passing an argument to the method, it's presumed data ready is defined something like:
- (void) dataReady:(NSData *)theData ...

Threading works in simulator but not on iPhone

I'm trying to display a UIActivityIndicatorView while background processing takes place.
The below simplified code works when I try it in the simulator(the alert is displayed)..but when I download it to my phone from Xcode, the background thread does not seem to get called at all. (the alert never gets displayed)
Any ideas?
-(void)viewDidLoad {
[self performSelectorInBackground:#selector(runInAnotherThread) withObject:nil];
}
-(void) runInAnotherThread {
NSAutoreleasePool *pool = [ [ NSAutoreleasePool alloc ] init ];
int i;
for(i=0;i < 1000 ;i ++){
NSLog(#"INDEX = %d", i);
}
[self performSelectorOnMainThread : # selector(backToMainThread ) withObject:nil waitUntilDone:NO];
[ pool release ];
}
-(void) backToMainThread {
UIAlertView *completeAlert = [[UIAlertView alloc]
initWithTitle:#"Back to main "
message: #"Success"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[completeAlert show];
[completeAlert release];
}
Have you you tried cleaning your build? I just ran your code on my device and simulator and it works as expected in both cases
Use NSOperation instead of raw thread manipulation. It abstracts all sorts of stuff for you (priority, autoreleasepools etc...). ? You can simply add some kind of delegate to your NSOperation subclass to get a callback when you need.
Thanks for replying so quickly!
It turned out that the issue was not in this code fragment at all. I was executing this code dependent on a value in the keychain. While my simulator's keychain has that value, my test iphone did not have this value.
Feel so silly for troubling all of you. But following up on the reply from nduplessis helped me narrow down the issue.