4

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.

Eric de Araujo
  • 243
  • 7
  • 16
  • does it work if you replace message:message with message:@"test"? – Ken Pespisa Jul 06 '09 at 18:44
  • No. same error. I even commented out the conditional message creation block. – Eric de Araujo Jul 06 '09 at 18:57
  • Very strange. Have you tried stepping through the debugger to figure out what line the error occurs on? I'm just curious if we're looking in the wrong place. – Ken Pespisa Jul 06 '09 at 20:22
  • It occurs at the very end of the method. You seem to be right Ken, it is something earlier in the method (see latest edit). – Eric de Araujo Jul 06 '09 at 20:28
  • Try adding a breakpoint at the top of your method. Then use the step into option to step through each line. You've narrowed it down to something above the UIAlertView line, but it's still too difficult to see what the problem is. Whichever line the debugger is stuck on when that EXC-BAD-ACCESS message appears, that will be your problem line. For a good video on the basics of debugging, see Jeff LaMarche's blog post on debugging: http://iphonedevelopment.blogspot.com/2009/03/debugging.html – Ken Pespisa Jul 06 '09 at 23:11

6 Answers6

8

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

j2emanue
  • 60,549
  • 65
  • 286
  • 456
  • For an example of what j2emanue means, see http://stackoverflow.com/questions/12468677/exc-bad-access-code-2-on-uialertview-in-ios6 – Ben Wheeler Jun 12 '13 at 20:05
4

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.

Sean McMains
  • 57,907
  • 13
  • 47
  • 54
  • Thanks for that. However, after removing [message release]; I get the same error. It must be something beyond the releasing of message string. – Eric de Araujo Jul 06 '09 at 18:53
  • Well, I'm baffled then. :) I'd try Ken's suggestion, and perhaps commenting out the "int result..." line to see if any of those might affect the problem one way or another. – Sean McMains Jul 06 '09 at 18:59
  • I believe the rule is methods containing alloc, retain or copy require you to release the object. – Mark Jul 06 '09 at 19:01
  • Neither replacing the message string or commenting the "int result" line causes the alert to show. Might it have anything to do that this is getting called after a "return" on the keyboard? I've called the [textfield resignFirstResponder]; but would the keyboard being visible effect an alert? – Eric de Araujo Jul 06 '09 at 19:05
  • Added a note about retaining the object. Thanks, Mark! – Sean McMains Jul 06 '09 at 19:07
1

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

hanleyp
  • 811
  • 9
  • 5
0

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.

Ben Gotow
  • 14,805
  • 3
  • 42
  • 47
  • 1
    I'm pretty sure that those do the same exact thing... `@"string"` and `[NSString stringWithString:@"string"]` both return non-retained NSStrings. – davidcann Feb 10 '10 at 07:03
0

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];
    }];
Azhar
  • 20,500
  • 38
  • 146
  • 211
0

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.

Jose Muanis
  • 547
  • 4
  • 13