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.
Related
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.
I'm creating an application that fetches a set of results from a database - I use MBProgressHUD to show the progress of the query with an animation. The method I use calls the animation while executing a method in another thread and once it's done, it hides the animation. My question is, after calling:
[HUD showWhileExecuting:#selector(getResults) onTarget:self withObject:nil animated:YES];
I would like to, if there are no results, display an alert stating this, and if there are, load the next view. So far, I have this code:
[HUD showWhileExecuting:#selector(getResults) onTarget:self withObject:nil animated:YES];
if(self.thereAreEvents) {
[self performSegueWithIdentifier:#"searchResults" sender:self];
} else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"No results" message:#"Sorry, there are no results for your search. Please try again." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alert show];
[alert release];
}
self.thereAreEvents gets set at the end of the getResults method. However, since that method gets called in another thread this line of execution continues and shows the alert, even though there are events in the database.
So, from here, I have two questions: What is the easiest way to implement a wait-signal mechanism in iOS and what is the most efficient way to implement this sort of mechanism in iOS?
Thanks!
You can use a busy wait loop for a quick and dirty solution:
__block BOOL finished = NO;
dispatch_async(/* global queue */, ^{
// …
finished = YES;
});
while (!finished) /* waiting */;
In “real” code it’s better to use a semaphore:
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_async(/* global queue */, ^{
// …
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_release(sempahore);
This is better than the busy loop because the blocked thread does not eat CPU time.
The best solution is to keep from blocking and redesign your code to work asynchronously. In your case, you should display a spinner and start downloading data. When the data is finished downloading, you should receive an asynchronous callback (either through a block or a target/action callback) and display the results or show the error alert. Blocking with a busy loop or a semaphore is a poor man’s solution in this case.
You could also consider an NSConditionLock.
So it'd be something like this on thread 1:
[conditionLock lockWhenCondition:kConditionOkayToProceed];
[conditionLock unlockWithCondition:kConditionGettingResults];
[HUD show...]
[conditionLock lockWhenCondition:kConditionResultsFetched];
[conditionLock unlockWithCondition:kConditionOkayToProceed];
And in the HUD:
- (void)show...
{
[conditionLock lockWhenCondition:kConditionGettingResults];
// stuff here
[conditionLock unlockWithCondition:kConditionResultsFetched];
}
Though a much better solution would be to pass a block or a target/selector to the HUD that it is to perform when results are fetched.
EDIT: so you'd end up with code like:
[HUD showWhileExecuting:#selector(getResults)
onTarget:self
withObject:nil
animated:YES
performWhenFinished:
^{
if(self.thereAreEvents) {
[self performSegueWithIdentifier:#"searchResults" sender:self];
} else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"No results" message:#"Sorry, there are no results for your search. Please try again." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alert show];
[alert release];
}
}];
And in the HUD:
- (void)showWhile... performWhenFinished:(dispatch_block_t)block
{
// all the other stuff you were going to do here, then
// eventually...
// if no guarantees, maybe just:
block();
// otherwise, if promised to dispatch to the main queue:
dispatch_async(dispatch_get_main_queue(), block);
}
With HUD having the extra intelligence to take a dispatch_block_t as a final argument and to call it when results are in (whether guaranteeing dispatch back to the main thread or otherwise).
This is your way:
Concurrency Programming Guide
Also: Synchronization
More sharp: Using Locks. I think the last one could give the best help.
Another simple approach, it's bad but works
NSAssert(![NSThread isMainThread], #"Do not run on MAIN thread");
while (yourCondition) { [NSThread sleepForTimeInterval:0.2]; }
Not sure if this is the best way to do it but I'd try using a while loop to observe some boolean with something like [NSThread sleepForTimeInterval:1]; inside the loop.
Maybe set a timeout as well.
you can use dispatch_time_t as..
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
<#code to be executed on the main queue after delay#>
});
In example code below UIAlertView is show after delay, but i need display it immediately
//metoda zapisuje komentrz na serwerze
-(void) saveAction {
UIAlertView *progressAlert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(#"imageGalleries.sendAction", #"") message:#" " delegate:self cancelButtonTitle:NSLocalizedString(#"alert.cancel", #"") otherButtonTitles:nil];
[progressAlert addSubview:progressView];
[progressAlert show];
// some long performance instructions
}
- (void)loadView {
[super loadView];
self.navigationItem.rightBarButtonItem = [NavButton buttonWithTitle:NSLocalizedString(#"sendImage.saveButtonTitle", #"") target:self action:#selector(saveAction)];
progressView = [[UIProgressView alloc] initWithFrame: CGRectMake(30.0f, 80.0f - 26, 225.0f, 10.0f)];
}
Why UIAlertView do not show immediately when I call saveAction?
If the “long performance instructions” following the alert code run on the main thread, they will block the alert from appearing. Read something about Cocoa run loops, that should make things more clear. (Essentially it could be said that all the UI instructions in your method are not performed immediately – they have to wait for the method to end, and then the main run loop picks them up and runs them.)
The code could better look like this:
- (void) startSomeLongOperation {
[self createAndDisplayProgressSpinner];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_NORMAL, 0), ^{
// …do something that takes long…
dispatch_async(dispatch_get_main_queue(), ^{
[self dismissProgressSpinner];
});
});
}
This moves the long operation into background, so that the main thread can continue executing immediately.
Have you considered using MBProgressHUD for long lasting operations? It'll wrap those calls into separate threads for you and is very flexible from functionality and UI perspectives
I don't think you need to call
[progressAlert addSubview:progressView];
Aside from that, it really should just appear...
If you show the alert in background thread, it will also have a delay.
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.
Update:
This has been answered. It was my own stupidity, possibly not worth reading any more of this question. lol.
Question:
Right so i have this UIViewController(master) subclass, it has a UIImagepickerController(camera), it also has a UIView(overlayView). Master setups the camera to be configured as a camera only with a custom cameraOverlay, hiding the custom controls e.t.c.
All appears to work fine apart from when i try to programatically take a picture. What happens is the overlayView calls the master and this triggers the take picture, then i hear the shutter sound and the iris closes, the camera appears to dismiss itself (i am defiantly not doing this in my code) and then my viewDidAppear gets called in my master again.
Anybody have any idea whats going on ?
-(void)viewDidLoad
{
NSLog(#"loading the view");
//if the camera is on the device
if ( [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])
{
//make one
camera = [[UIImagePickerController alloc] init];
//setup some settings that we need
camera.sourceType = UIImagePickerControllerSourceTypeCamera;
camera.showsCameraControls = NO;
camera.navigationBarHidden = NO;
camera.toolbarHidden = YES;
camera.cameraViewTransform = CGAffineTransformScale(camera.cameraViewTransform, 1.03, 1.03);
//show it
overlayView = [[OverlayView alloc] initWithFrame:CGRectMake(0,0,320,480) withDelegate:self andController:self];
camera.cameraOverlayView = overlayView;
camerashowing=NO;
}
else
{
alert = [[UIAlertView alloc] initWithTitle:#"No Camera Detected" message:#"The camera is broken or your device has no camera. Please close the application" delegate:self cancelButtonTitle:nil otherButtonTitles:nil, nil];
[alert show];
[alert release];
}
}
-(void)viewDidAppear:(BOOL)animated
{
if (!cameraShowing)
{
NSLog(#"going to show camera");
[self presentModalViewController:camera animated:NO];
camerashowing = YES;
}
}
-(void)releaseShutter
{
[overlayView toolbarShowWarning];
NSLog(#"going to show camera: %#", self);
[camera takePicture];
}
After some help advice from people in the answers i can say that the camera is not being released.
I have also managed to stop the exec_bad_access by stopping it from calling [presentmodal....] for a second time by checking a bool value in the viewDidAppear Method.
I still have the issue where the modal view disapears, any help, again lol ??
I think you're missing a camera.delegate = self;
For any EXC_BAD_ACCESS errors, you are usually trying to send a message to a released object. The BEST way to track these down is use NSZombieEnabled.
This works by never actually releasing an object, but by wrapping it up as a "zombie" and setting a flag inside it that says it normally would have been released. This way, if you try to access it again, it still know what it was before you made the error, and with this little bit of information, you can usually backtrack to see what the issue was.
It especially helps in background threads when the Debugger sometimes craps out on any useful information.
VERY IMPORTANT TO NOTE however, is that you need to 100% make sure this is only in your debug code and not your distribution code. Because nothing is ever released, your app will leak and leak and leak. To remind me to do this, I put this log in my appdelegate:
if(getenv("NSZombieEnabled") || getenv("NSAutoreleaseFreedObjectCheckEnabled"))
NSLog(#"NSZombieEnabled/NSAutoreleaseFreedObjectCheckEnabled enabled!");
If you need help finding the exact line, Do a Build-and-Debug (CMD-Y) instead of a Build-and-Run (CMD-R). When the app crashes, the debugger will show you exactly which line and in combination with NSZombieEnabled, you should be able to find out exactly why.
Check the value of the camera member variable before you try and display it:
NSLog(#"going to show camera: %#", camera);
I suspect it might be being released somewhere, but as coneybeare NSZombieEnabled will let you track it down.