UIAlertView button results in a child NSThread - objective-c

I would like a user to get a prompt screen, it would have yes and no options.
The problem is the UIAlertView cannot be called from child thread, if I call it from child thread I am getting a runtime error(EXC_BAD_ACCESS). I am using NSThread on IOS6.1
This is the code that i am using
-(void) construct
{
NSThread *initThread =[[NSThread alloc]initWithTarget:self selector:#selector(error) object:nil];
[initThread start];
}
- (void) error
{
//make sure it runs on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Save"
message:#"Enter File Name"
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"OK", nil];
alertView.alertViewStyle = UIAlertViewStylePlainTextInput;
[alertView show];
});
}
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
NSLog(#"Alert View dismissed with button at index %d",buttonIndex);
switch (alertView.alertViewStyle)
{
case UIAlertViewStylePlainTextInput:
{
UITextField *textField = [alertView textFieldAtIndex:0];
NSLog(#"Plain text input: %#",textField.text);
} break;
case UIAlertViewStyleSecureTextInput:
{
UITextField *textField = [alertView textFieldAtIndex:0];
NSLog(#"Secure text input: %#",textField.text);
} break;
case UIAlertViewStyleLoginAndPasswordInput:
{
UITextField *loginField = [alertView textFieldAtIndex:0];
NSLog(#"Login input: %#",loginField.text);
UITextField *passwordField = [alertView textFieldAtIndex:1];
NSLog(#"Password input: %#",passwordField.text);
}break;
default: break;
}
}
This is the error that I am getting:
[NSURLCacheInternal alertView:didDismissWithButtonIndex:]: unrecognized selector sent to instance 0x8653630
2013-07-26 16:12:35.738 iOSTrack[7455:c07] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSURLCacheInternal alertView:didDismissWithButtonIndex:]: unrecognized selector sent to instance 0x8653630'
*** First throw call stack:
(0x1e7a012 0x1796e7e 0x1f054bd 0x1e69bbc 0x1e6994e 0x7c0e13 0x408d66 0x408f04 0x20e7d8 0x266b014 0x265b7d5 0x1e20af5 0x1e1ff44 0x1e1fe1b 0x1dd47e3 0x1dd4668 0x3caffc 0x21cd 0x20f5)
libc++abi.dylib: terminate called throwing an exception

Most likely what is happening is that your UIAlertView is a local variable and it's getting destroyed before the delegate has a chance to run. You need to keep a reference to it around long enough such that your delegate has a chance to run and do it's work. Then you are in the clear. This concept applies to both ARC and manual memory managed based code.
See this related question: UIAlertViewDelegate class "self" instance gets dealloc'd before button gets pressed

The code I tried actually worked fine for me
The only difference is
[alertView show] => [alertView show]; you miss ;,a typo in question i guess
running the code in seperate thread
dispatch_queue_t myQueue = dispatch_queue_create("My Queue",NULL);
dispatch_async(myQueue, ^{
// Perform long running process
[self error];
});
- (void) error {
//make sure it runs on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Save"
message:#"Enter File Name"
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"OK", nil];
alertView.alertViewStyle = UIAlertViewStylePlainTextInput;
[alertView show];
});
}

Related

UIAlertView makes the program crash

I've got a crash:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[UIKeyboardTaskQueue performTask:] may only be called from the main thread.'
And I could not find a solution for 2 days.
And here is the code:
[alert dismissWithClickedButtonIndex:0 animated:YES];
UIAlertView *noTicketAlert = [[UIAlertView alloc] initWithTitle:#"Aradığınız kriterlere uygun bilet bulunamadı!" message:nil delegate:self cancelButtonTitle:#"Tamam" otherButtonTitles: nil];
[noTicketAlert show];
I triggered this error by attempting to display an alert from a background thread. Fixed like this:
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:...
[alertView show];
});
I got this error when presenting a UIAlertView normally (no funny button override stuff). It turned out that I was presenting it twice in quick succession. The fix in my case was to remove the erroneous duplicate call.
If you do need to present two alert views at close to the same time, and you get this error, then a fix that works (and addresses the error message itself) is to run the code on the main thread:
[[NSOperationQueue mainQueue] addOperationWithBlock:^
{
// Your code that presents the alert view(s)
}];
Yes, I've found the solution and I share that with you guys.
I tried to override the dismissWithClickedButtonIndex function, and sent unique buttonIndexes
like 9999 for each of my alerts.
That is,
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
[self viewWillDisappear:YES];
if(buttonIndex == 9999) {
noTicketAlert = [[UIAlertView alloc] initWithTitle:#"Aradığınız kriterlere uygun bilet bulunamadı!" message:nil delegate:self cancelButtonTitle:#"Tamam" otherButtonTitles: nil];
[noTicketAlert show];
}
}
and if I want to display the noticketAlert, I call this method like :
[alert dismissWithClickedButtonIndex:9999 animated:YES];
If you have a custom button make sure you implement the delegate method:
- (BOOL)alertViewShouldEnableFirstOtherButton:(UIAlertView *)alertView
{
return YES;
}
if this selector is not found then the program with crash..
For those looking for the Swift 2 answer to this issue, I ran into a similar problem and solved it with #Dave Batton's solution
dispatch_async(dispatch_get_main_queue(), {
self.performSegueWithIdentifier("loginSegue", sender: self)
})

error: address doesn't contain a section that points to a section in a object file

Hi i have searched here on the forum but no help found so i am posting it new. Here is the scenario, i am creating a mfmailcomposeviewcontroller in the main rootviewcontroller, i am displaying it by calling presentviewcontroller but when it is dismissed i get this error :
error: address doesn't contain a section that points to a section in a object file
The code i am using is given below:
-(void) mailButtonTapped
{
if ([MFMailComposeViewController canSendMail]) {
mailViewController_ = [[MFMailComposeViewController alloc] init];
mailViewController_.mailComposeDelegate = self;
[mailViewController_ setSubject:#"Try ..."];
[mailViewController_ setMessageBody:#"Hey I just tried ..." isHTML:NO];
NSData *videoData = [NSData dataWithContentsOfURL:movieURL_];
[mailViewController_ addAttachmentData:videoData mimeType:#"video/quicktime" fileName:#"Video.mov"];
[self presentViewController:mailViewController_ animated:YES completion:nil];
}
else {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Sharing Not Possible" message:#"Configure your mail to send the mail" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alertView show];
[alertView release];
}
}
-(void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error
{
NSString *title = #"Email";
NSString *msg = nil;
if (result == MFMailComposeResultFailed)
msg = #"Unable to send, check your email settings";
else if (result == MFMailComposeResultSent)
msg = #"Email Sent Successfully!";
else if (result == MFMailComposeResultCancelled || result == MFMailComposeResultSaved)
msg = #"Sending Cancelled";
UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:title message:msg delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
[alertView release];
[self dismissViewControllerAnimated:YES completion:nil];
}
After dismissing i receive the error:
error: address doesn't contain a section that points to a section in a object file
Please help me
I also had this error but with another scenario. I had a block property defined with #property (assign, nonatomic).
To fix this issue, I declared my block property with #property (copy, nonatomic).
Cheers
This error happens when an object / pointer is being accessed that doesn't exist anymore . And can also cause other bad access, 0x00000 value accessed etc.. errors.
So you are deleting/releasing a pointer , and then accessing later .
From looking at the code, and this is just a guess without debugging, you set the second AlertView's delegate to self, but then immediately dismiss the viewcontroller.
Try dismissing after the alert view is dismissed or the button is pressed, or maybe just setting the AlertView delegate to nil.
Even if that's not exactly the error, the main reason is somewhere you are releasing an object then trying to call a function or access it.
You can use it like this:
MFMailComposeViewController *mailViewController_ = [[MFMailComposeViewController alloc] init];
mailViewController_.mailComposeDelegate = self;
[mailViewController_ setSubject:#"Try ..."];
[mailViewController_ setMessageBody:#"Hey I just tried ..." isHTML:NO];
NSData *videoData = [NSData dataWithContentsOfURL:movieURL_];
[mailViewController_ addAttachmentData:videoData mimeType:#"video/quicktime" fileName:#"Video.mov"];
[self presentViewController:mailViewController_ animated:YES completion:nil];
[mailViewController_ release];
I also had this problem but caused by a very silly error, i wrote a property called frameon a class that inherits from UIView (it was a UITableViewCell but i think this would happen with every class that inherits from UIView) this overwrote the original frame property and caused this error.
Fixed just by changing property name.
The completion expects a block to be called when the animation of dismissal is completed.
Just remove "completion:nil" and it should work!
Regards.

iOS EXC_BAD_ACCESS error in class

I'v encountered EXC_BAD_ACCESS error in my app. Or to be more specific in one of my classes. It is Custom UIAlertView class. I couldn't catch when it throws EXC_BAD_ACCESS in usage. Sometimes it works great just as expected, and in all suden it craches... Here is whole class
#implementation AlertPassword
int counter = 3;
#synthesize done;
#synthesize alertText;
#synthesize msg;
- (void) showAlert :(NSString*) title
{
if(counter != 3){
if(counter == 1)
{
NSString *msgs = #"Last warning";
msg = msgs;
}
else
{
NSString *msgs = [NSString stringWithFormat:#"WRONG PIN. %d times remaining",counter];
msg = msgs;
}
}
else
{
NSString *msgs = #"Enter your pin";
msg = msgs;
}
UIAlertView * alert = [[UIAlertView alloc] initWithTitle:#"Security" message:msg delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles: nil];
_alert = alert;
_alert.alertViewStyle = UIAlertViewStyleSecureTextInput;
alertText = [_alert textFieldAtIndex:0];
alertText.keyboardType = UIKeyboardTypeNumberPad;
alertText.placeholder = #"Pin";
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(controlTextDidChange:)
name:UITextFieldTextDidChangeNotification object:alertText];
[_alert show];
[_alert release];
[[NSNotificationCenter defaultCenter] removeObserver:UITextFieldTextDidChangeNotification];
}
- (void)controlTextDidChange:(NSNotification *)notification {
{
NSString *pin = [[NSUserDefaults standardUserDefaults] stringForKey:#"Pin"];
if ([notification object] == alertText)
{
if (alertText.text.length == pin.length)
{
if(counter != 0)
{
if([alertText.text isEqualToString:pin])
{
[_alert dismissWithClickedButtonIndex:0 animated:NO];
[self.tableViewController openSettings];
counter = 3;
}
else
{
counter--;
[_alert dismissWithClickedButtonIndex:0 animated:NO];
[self showAlert:#""];
}
}
else
{
[_alert dismissWithClickedButtonIndex:0 animated:NO];
[[NSUserDefaults standardUserDefaults] setObject:NULL
forKey:#"Telephone"];
[[NSUserDefaults standardUserDefaults] setObject:NULL
forKey:#"Pin"];
[[NSUserDefaults standardUserDefaults] synchronize];
UIAlertView *av = [[UIAlertView alloc] initWithTitle:#"" message:AMLocalizedString(#"EraseData", nil) delegate:nil cancelButtonTitle:AMLocalizedString(#"Ok", nil) otherButtonTitles:nil];
counter = 3;
[av show];
[av release];
}
}
}
}
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
NSString *pincheck = [[NSUserDefaults standardUserDefaults] stringForKey:#"pinCheck"];
if (buttonIndex == 0)
{
if(pincheck.intValue == 1)
{
NSLog(#"app kill");
exit(0);
}
else
{
NSLog(#"dismiss");
}
}
}
#end
Here is where i initialize and use this class.
case 5:
NSLog(#"Add remove");
if (pincheck != NULL && pin != NULL){
if([pincheck isEqualToString:#"0"])
{
AlertPassword *alert = [AlertPassword alloc];
alert.tableViewController = self;
NSString *msg = #"Enter your pin code to access:";
[alert showAlert:msg];
// [alert release];
}
break;
}
else
{
NSLog(#"Is null");
[Menu load2View:self];
}
break;
I though maybe it was because i do not release alert. But adding [alert release]; Made to have EXC_BAD_ACCESS directly after the user tries to enter something. Without [alert release]; it works. But sometimes it craches with EXC_BAD_ACCESS
Also sometimes it gets
2012-11-08 12:11:27.451 kodinisRaktas[2485:19d03] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSMallocBlock__ dismissWithClickedButtonIndex:animated:]: unrecognized selector sent to instance 0x947aae0'
But I also have no idea why this happends
Please help, I'm pretty new to objective-c and ios, and I have no idea how to get rid of this, I guess someone with a bit of experience will see whats wrong in my code.
I'v just saw, that EXC_BAD_ACCESS or unrecognized selector throws if you push cancel for 4-5 times or more, and then try to type something.
EXC_BAD_ACCESS is mostly due to bad memory handling. The alert has most likely become an zombie... I would have the alert as a property with strong/retain. You should hold on to it while displaying. Not release after "show".
When you set up the first you can do like this
_alert = [[UIAlertView alloc] initWithTitle:#"Security" message:msg delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles: nil]; // retain count: 1
Note calling "show" will also retain it, but that does not change the fact that you need to too.
[_alert show]; // retain count: 2
Wait for the delegate callback and release it.
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
[_alert release], _alert = nil; // retain count in next run will be 0
}
Tip: It may be easier to handle if you use UIAlertView combined with blocks. http://gkoreman.com/blog/2011/02/15/uialertview-with-blocks/
I don't see where you initialize alert, anyway I suppose it's a class field, so retain it.If it's not a class instance variable, make it be so, this way you will always have a pointer to it.
[__NSMallocBlock__ dismissWithClickedButtonIndex:animated:]
You are sending this message to a raw malloc block, so probably alert has been released and points to a memory used for something else, something that's not an objc object.
Try to retain alert and see what happens.
May be you are passing wrong values to this "dismissWithClickedButtonIndex:animated:" method which is not recognizing the value signature please do a double check for that;
the excepted answer does make sense and is likely the cause
but what is this?
[NSNotificationCenter defaultCenter] removeObserver:UITextFieldTextDidChangeNotification];

UIAlertView Loading indicator doesn't work in a secondary thread

I have following code for the UIAlertView Loading indicator which is not working and giving me
- (void) launchActivity
{
//some logic...
[NSThread detachNewThreadSelector:#selector(updateFilterProgress) toTarget:self withObject:nil];
}
- (void) updateFilterProgress {
if ((internetStatus != ReachableViaWiFi) && (internetStatus != ReachableViaWWAN))
{
UIAlertView *myAlert = [[[UIAlertView alloc] initWithTitle:#"No Internet Connectivity" message:#"This app require an internet connection via WiFi or cellular network to work." delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil] autorelease];
[myAlert show];
}
else{
UIAlertView *alertMe = [[[UIAlertView alloc] initWithTitle:#"Loading..." message:nil delegate:nil cancelButtonTitle:nil otherButtonTitles: nil] autorelease] ;
//tried this way by placing below line....no result
[alertMe performSelectorOnMainThread:#selector(show) withObject:nil waitUntilDone:YES];
//[alertMe show];
//some logic...
}
Updated:
On main thread I am calling web-service and loading data. hence I have given another thread for Loading UIAlertView and it was working with iOS 4,5. but its crashing in iOS 6. If I am placing AlerView on main thread then while loading nothing shows but after getting data loaded AlertView shows Loading Indicator for few seconds. Any suggestion...
You are showing the alert from the detached thread while you must do it from the main thread, either use GCD or performSelectorOnMainThread.
On the main thread you usually want to perform only the UI update, all the complex calculations and data loading are to be executed in the detached threads. If you try to load the data in the main thread, the UI will no be responding during the loading. So that is a good practice to load the data in the detached thread, on the main thread you show the alert as you need on the loading start and dismiss it on the loading (and parsing) finished, calling the content UI update as well:
It's a bad practise.
Apple docs say that you need to handle the UI elements on the main thread.
I think the issue is with this line:
[NSThread detachNewThreadSelector:#selector(updateFilterProgress) toTarget:self withObject:nil];
You won't handle the UI element on other threads rather than on main thread.
Use:
[self updateFilterProgress];
Or use like:
[yourAlert performSelectorOnMainThread:#selector(show) withObject:nil waitUntilDone:YES];
Also I checked with your code. There is one error popping up:
Error is: No visible #interface for 'UIAlertView' declares the selector 'performSelectorOnCurrentThread:withObject:waitUntilDone:'
The performSelectorOnMainThread is working perfectly for me.
Try this
- (void) launchActivity
{
//some logic...
[NSThread detachNewThreadSelector:#selector(updateFilterProgress) toTarget:self withObject:nil];
}
- (void) updateFilterProgress {
if ((internetStatus != ReachableViaWiFi) && (internetStatus != ReachableViaWWAN))
{
[self performSelectorOnMainThread: #selector(showAlertForNoNetConnect)];
}
else{
[self performSelectorOnMainThread: #selector(showAlertForLoading)];
//some logic...
}
- (void) showAlertForNoNetConnect
{
UIAlertView *myAlert = [[[UIAlertView alloc] initWithTitle:#"No Internet Connectivity" message:#"This app require an internet connection via WiFi or cellular network to work." delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil] autorelease];
[myAlert show];
}
- (void) showAlertForLoading
{
UIAlertView *alertMe = [[[UIAlertView alloc] initWithTitle:#"Loading..." message:nil delegate:nil cancelButtonTitle:nil otherButtonTitles: nil] autorelease] ;
[alertMe show];
}
You have to call all the UIKit elements in the main thread. That's what the problem is.
Hope this helps. Happy Coding. :)

Two UIAlertView consecutively in didFinishLaunchingWithOptions

I want two alert views to show up only when the user opens my application for the first time -- the second to appear after the first is dismissed. I have it set up to only show the UIAlertViews when it has not been shown before and I do not need help with this. I need help figuring out how to display two alert views in a row when this is the case.
-(void) alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex does not work for me.
Here is the code I have -- remember this is in didFinishLaunchingWithOptions:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
BOOL didFirstLaunch = [defaults boolForKey:#"DidFirstLaunch"];
if (!didFirstLaunch) {
[defaults setBool:YES forKey:#"DidFirstLaunch"];
UIAlertView *successAlert = //not important
[successAlert show];
[successAlert release];
//Somehow show second alert after the first is dismissed
}
I'm gonna post a very simple solution using GCD & blocks (GCD part is just in case the alert view is created on another thread then the main thread, callback should be safe to perform on the main thread). Remember, I just coded this in like 5 mins, so you definitely should work on improving the code. One thing that's a bit ugly is the delegate parameter that is overridden in my subclass. The interface of the subclass could be changed a bit to make it more obvious of what happens ...
Anyway, here goes ...
First create a subclass of UIAlertView, make it look somewhat like the following ...
#interface FSAlertView () <UIAlertViewDelegate>
#property (nonatomic, copy) void (^dismissHandler)(NSInteger buttonIndex);
#end
#implementation FSAlertView
#synthesize dismissHandler = _dismissHandler;
- (void)showWithDismissHandler:(void (^)(NSInteger buttonIndex))dismissHandler
{
self.dismissHandler = dismissHandler;
self.delegate = self;
[self show];
}
// Alert view delegate
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
dispatch_async(dispatch_get_main_queue(), ^ {
if (_dismissHandler)
{
_dismissHandler(buttonIndex);
}
});
}
Now in the app we can create alert views like the following ...
FSAlertView *alert1 = [[FSAlertView alloc] initWithTitle:#"Alert 1"
message:#"Some message"
delegate:nil
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"Show 2nd Alert", nil];
[alert1 showWithDismissHandler:^ (NSInteger buttonIndex) {
NSLog(#"button pressed: %d", buttonIndex);
if (buttonIndex == 1)
{
UIAlertView *alert2 = [[UIAlertView alloc] initWithTitle:#"Alert 2"
message:#"Hi!"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert2 show];
}
}];
If i understand your question correctly , then this may help:
UIAlertView *firstAlert = [[UIAlertView alloc] initWithTitle:#"Alert 1" message:nil delegate:self cancelButtonTitle:nil otherButtonTitles:nil, nil];
[firstAlert show];
[self performSelector:#selector(test:) withObject:firstAlert afterDelay:2];
[firstAlert release];
UIAlertView *secondAlert = [[UIAlertView alloc] initWithTitle:#"Alert 2" message:nil delegate:self cancelButtonTitle:nil otherButtonTitles:nil, nil];
[secondAlert show];
[self performSelector:#selector(test:) withObject:secondAlert afterDelay:2];
[secondAlert release];
-(void)test:(UIAlertView*)alert{
[alert dismissWithClickedButtonIndex:-1 animated:YES];
}
This will show two alert views one after the other.
NOTE: I am not sure if you are dismissing the alerts with cancel button so i am dismissing them automatically after few seconds.
Try this:
UIAlertView *firstAlert = [[UIAlertView alloc] initWithTitle:#"Title" message:#"Message" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"Ok", nil];
[firstAlert setTag:444];
[firstAlert show];
firstAlert = nil;
AlertView Delegate Method:
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
switch (alertView.tag) {
case 444:
{
//Cancel ButtonIndex = 0
if (buttonIndex == 1) {
UIAlertView *secondAlert = [[UIAlertView alloc] initWithTitle:#"Title 2" message:#"Message2" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"Dismiss", nil];
[secondAlert setTag:555];
[secondAlert show];
secondAlert = nil;
}
}
break;
case 555:
{
if (buttonIndex == 1) {
NSLog(#"Code Here");
}
}
break;
}
}