dismissViewControllerAnimated:completion: has a couple second delay - iphone

dismissViewControllerAnimated:completion: is working fine in my application, except for the delay between the dismissal.
[api loginWithUsername:[dict objectForKey:#"username"] andPassword:[dict objectForKey:#"password"] andSuccessBlock:^(id json) {
NSLog(#"DONE... %#", [json objectForKey:#"status"]);
NSString *status = [json objectForKey:#"status"];
if([status isEqualToString:#"ok"]){
app.user = [json objectForKey:#"data"];
[self dismissViewControllerAnimated:YES completion:nil];
}else{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:#"could not log you in" delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[alert show];
}
}];
In my console I can see echo'ed "DONE... ok", meaning the callback block was executed, however about 3-4 seconds later the modal view is finally dismissed.
What could be causing this delay?

If you don't guarantee your UI code is running on Main Thread it might run on some other and in that case, you will experience a few seconds delay!
You can add this to make sure that dismissal is ran on main thread:
dispatch_async(dispatch_get_main_queue(), ^{
[self dismissViewControllerAnimated:YES completion:nil];
});
Generally, this is not an issue as most of your code will already be running on main thread, since we mostly add code that runs from UIKit methods, like viewDidLoad and such. Those methods are guaranteed to be ran on the main thread.
The issue arises when you end up running code on another thread. One case where that can happen is for example on a completion block invocation of a networking library, where the request is done on background.

check 'viewWillAppear' of the ParentViewController and 'viewWillDisappear' of the currentViewcontroller.
Make sure this two functions does not have any heavy calculations and memory allocations within.

Try changing
NSString *status = [json objectForKey:#"status"];
NSLog(#"DONE... %#", status);
and then try
if([status isEqualToString:#"ok"]){
[self dismissViewControllerAnimated:YES completion:nil];
app.user = [json objectForKey:#"data"];
}else{
As I don't know how big your JSON object is it could be taking a moment to respond and as you're calling it again after the log statement and the doing something else before saying dismiss it could be that.

Related

Speed up the response retriever from server in iphone

I am using following code to get results from server
NSString *queryString = #"MyString"
NSString *response = [NSString stringWithContentsOfURL:[NSURL URLWithString:queryString] encoding:NSUTF8StringEncoding error:&err];
NSLog(#"%#",response);
if (err != nil)
{
UIAlertView *alert = [[UIAlertView alloc]initWithTitle: #"Error"
message: #"An error has occurred. Kindly check your internet connection"
delegate: self
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[alert show];
[indicator stopAnimating];
}
else
{
//BLABLA
}
The problem with this code is ,If server shows lag and it takes lets say 3 seconds to get this response
NSString *response = [NSString stringWithContentsOfURL:[NSURL URLWithString:queryString]
For 3 seconds my iPhone screen is jammed. How can i make it run in background so that it won't slow down or jam the mobile
Regards
What you are doing is sending HTTP request from the main thread. that will jam up the UI as you said. You need to spawn a background thread and make a request to your server, when the response comes back then you need to update the UI from the main thread. This is a common pattern in UI coding.
__block__ NSString *response;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
//your server url and request. data comes back in this background thread
response; = [NSString stringWithContentsOfURL:[NSURL URLWithString:queryString] encoding:NSUTF8StringEncoding error:&err];
dispatch_async(dispatch_get_main_queue(), ^{
//update main thread here.
NSLog(#"%#",response);
if (err != nil)
{
UIAlertView *alert = [[UIAlertView alloc]initWithTitle: #"Error"
message: #"An error has occurred."
delegate: self
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[alert show];
[indicator stopAnimating];
}
});
});
You can also use performSelectorInBackground:withObject: to spawn a new thread, then the performed selector is responsible for setting up the new thread's autorelease pool, run loop and other configuration details – see "Using NSObject to Spawn a Thread" in Apple's Threading Programming Guide.
You'd probably be better off using Grand Central Dispatch as I posted above. GCD is a newer technology, and is more efficient in terms of memory overhead and lines of code.
You could use ASIHTTPRequest which is my favorite for getting and sending information.

Incrementing `static int` causes SIGSEGV SEGV_ACCERR

I'm debugging a crash reported as:
Exception Type: SIGSEGV
Exception Codes: SEGV_ACCERR
The crash is happening on the line that does numberOfFails++.
The app uses ASIHTTP. I personally much prefer using NSURLConnection. I'd never automatically repeat a request for NSURLConnection if it failed because I've never seen it fail when it shouldn't. I'd rather just give the UI a refresh button or show a UIAlertView with a button to try again or something like that.
Anyway, to cooperate with other team members, I'm looking to fix this issue without replacing ASIHTTP with NSURLConnection for now.
The request is being started with something like:
- (void)getResources:(CLLocation *)location withQuery:(NSString *)query {
NSURL *url = [NSURL URLWithString:[NSString stringWithString:#"https://example.com/"]];
self.resourcesAPIRequest = [ASIFormDataRequest requestWithURL:url];
[resourcesAPIRequest setPostValue:[Model instance].oauth_token forKey:#"oauth_token"];
[resourcesAPIRequest setPostValue:[[NSNumber numberWithDouble:location.coordinate.latitude] stringValue] forKey:#"latitude"];
[resourcesAPIRequest setPostValue:[[NSNumber numberWithDouble:location.coordinate.longitude] stringValue] forKey:#"longitude"];
[resourcesAPIRequest setPostValue:query forKey:#"query"];
[resourcesAPIRequest setDelegate:self];
[resourcesAPIRequest setDidFinishSelector:#selector(resourcesAPIReturned:)];
resourcesAPIRequest.userInfo = [NSDictionary dictionaryWithObjectsAndKeys:NSStringFromSelector(#selector(getResources:withQuery:)), #"repeatSelector", location, #"argument1", query, #"argument2", nil];
[resourcesAPIRequest startAsynchronous];
}
One thing I noticed is : <ASIHTTPRequestDelegate> is not in the header file, but this callback method is still being called OK:
#define maximumNumberOfFails 50
- (void)requestFailed:(ASIFormDataRequest *)request {
static int numberOfFails = 0;
if (numberOfFails < maximumNumberOfFails) {
[NSThread sleepForTimeInterval:sleepTimeInSeconds];
if ([request.userInfo objectForKey:#"argument2"]) {
[self performSelector:NSSelectorFromString([request.userInfo objectForKey:#"repeatSelector"]) withObject:[request.userInfo objectForKey:#"argument1"] withObject:[request.userInfo objectForKey:#"argument2"]];
} else if ([request.userInfo objectForKey:#"argument1"]) {
[self performSelector:NSSelectorFromString([request.userInfo objectForKey:#"repeatSelector"]) withObject:[request.userInfo objectForKey:#"argument1"]];
} else {
[self performSelector:NSSelectorFromString([request.userInfo objectForKey:#"repeatSelector"])];
}
numberOfFails++;
} else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Problem" message:#"There was a problem connecting to the servers. Please make sure you have an Internet connection." delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[alert show];
[alert release];
numberOfFails = 0;
}
}
Also, I think that static int numberOfFails should be static NSUInteger numberOfFails. And, I noticed that the request is started with startAsynchronous. Is static int numberOfFails atomic? That may be why we're getting the error SEGV_ACCERR (Invalid permissions for mapped object).
Thoughts?
The problem likely has nothing to do with your static variable.
Does requestFailed: execute on the main thread, or in a background thread?
If it's on a background thread, you'll need to use performSelectorOnMainThread:withObject:.
If it's on the main thread, you may need to take a pass through the runloop before executing a new HTTP request. To do that, use performSelector:withObject:afterDelay:, and pass '0.0' as the delay.
You'll notice that both those methods allow only a single method parameter. Typically, you pass your parameters in a NSDictionary rather than try to parse out the number of parameters beforehand, like you're doing.

iPhone: Using Alerts to Help Debugging

I've been building a rather complex system and there's come the time now where I want more concise debugging. I would like to display the contents of a variable (for this example an NSString called v_string) in a notification window (the kind of window that appear when you receive an SMS text).
Is there an easy way to just call an alert with a variable?
Thanks in Advance,
Dan
NSLog does not do? If not (like if you need to debug an application running on a disconnected device), you can extend the UIAlertView with a category:
#implementation UIAlertView (Logging)
+ (void) log: (id <NSObject>) anObject
{
NSString *message = [anObject description];
UIAlertView *alert = [[self alloc] initWith…];
[alert show];
[alert release];
}
And then in code:
NSString *anInterestingString = …;
[UIAlertView log:anInterestingString];
When you build the string to display in the alert window, simply append your variable's string represenation using stringByAppendingString.
Alert window is cumbersome. Use NSLog instead:
NSLog(#"Variable is: %#", v_string);
And in Xcode's console you will see that text.
UIAlertView *message = [[UIAlertView alloc] initWithTitle:#"My Debug String" message:v_string delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[message show];
[message release];
I think this way you can see what you want.
But, as zoul said, why not to use NSLog(#"my var: %#", v_string); ?
Hope that it helps.

UIAlertView Pops Up Three Times per Call Instead of Just Once

I am getting odd behavior from an NSAlert in two different parts of my program. The behavior is:
Alert appears and then spontaneously disappears.
Alert reappears and then remains until dismissed by user i.e. normal behavior.
Alert reappears again.
This behavior only occurs the first time the method that displays the alert is called. After that first time, it behaves normally.
Here is the code for the one of the parts in which the behavior occurs:
UIAlertView * locationAlert = [[UIAlertView alloc] initWithTitle:#"You are in the right place." message:nil delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[locationAlert show];
[locationAlert release];
Or if you prefer, with a bit more context:
- (IBAction)locateMe {
NSLog(#"About to check location");
locMan = [[CLLocationManager alloc] init];
locMan.delegate = self;
locMan.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
locMan.distanceFilter = 1609; //1 mile
[locMan startUpdatingLocation];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation * )oldLocation {
if (newLocation.horizontalAccuracy >= 0) {
CLLocation *airportLocation = [[[CLLocation alloc] initWithLatitude:51.500148 longitude:-0.204669] autorelease];
CLLocationDistance delta = [airportLocation getDistanceFrom: newLocation];
long miles = (delta * 0.000621371) + 0.5; //metres to rounded mile
if (miles < 3) {
UIAlertView * locationAlert = [[UIAlertView alloc] initWithTitle:#"You are in the right place." message:nil delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[locationAlert show];
[locationAlert release];
[locMan stopUpdatingLocation];
} else {
UIAlertView * locationAlert = [[UIAlertView alloc] initWithTitle:#"You are not in the right place." message:nil delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[locationAlert show];
[locationAlert release];
[locMan stopUpdatingLocation];
}
}
}
- (void) locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
UIAlertView * locationAlert = [[UIAlertView alloc] initWithTitle:#"Error." message:error.code delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[locationAlert show];
[locMan release];
locMan = nil;
}
Any ideas? Thanks.
Edit---------
The other place this happens is:
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
NSString * errorString = [NSString stringWithFormat:#"Unable to download feed from web site (Error code %i )", [parseError code]];
NSLog(#"error parsing XML: %#", errorString);
UIAlertView * errorAlert = [[UIAlertView alloc] initWithTitle:#"Error loading content" message:errorString delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[errorAlert show];
}
For context the first case is in the AppDelegate and the second in the view controller for the 1st tab view. The second problem occurs every time the xml is reloaded when there is no internet connection. The first one only occurs the first time the function is called.
Edit-----
If I move the alert it works. Unfortunatly this is not where I want it!
- (IBAction)locateMe {
UIAlertView * locationAlert = [[UIAlertView alloc] initWithTitle:#"You are in the right place." message:nil delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[locationAlert show];
/*
NSLog(#"About to check location");
locMan = [[CLLocationManager alloc] init];
locMan.delegate = self;
locMan.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
locMan.distanceFilter = 1609; //1 mile
[locMan startUpdatingLocation];*/
}
Update:
I set some NSLog entries and discovered that despite the addition of [locMan stopUpdatingLocation] the didUpdateToLocation function was running multiple times.
I guess the spontaneous disappearance happens because the alert view is called again and the programme clears the first instance to make way for the second automatically.
Any ideas as to why [locMan stopUpdatingLocation] doesn't work would be appreciated but in the mean time I just moved the declaration of the locationAlert out of the function (so it is global), set it in the initial locate me function and use the following the first time it is called:
[locationAlert show];
locationAlert = nil;
That way it works perfectly.
You're not turning off your location manager when you first show the alert. As the location is refined by the device (ie, the accuracy is increased), your callback will be (potentially) called multiple times. You should use [locMan stopUpdatingLocation] after your alert display.
I set some NSLog entries and discovered that despite the addition of [locMan stopUpdatingLocation] the didUpdateToLocation function was running multiple times.
I guess the spontaneous disappearance happens because the alert view is called again and the programme clears the first instance to make way for the second automatically.
Any ideas as to why [locMan stopUpdatingLocation] doesn't work would be appreciated but in the mean time I just moved the declaration of the locationAlert out of the function (so it is global), set it in the initial locate me function and use the following the first time it is called:
[locationAlert show];
locationAlert = nil;
That way it works perfectly.
I think the NSAlert disappearing on its own is the key to solving this.
It's simple to explain why an alert displays unexpectedly i.e. it's just been called unexpectedly. However, it's not so common to programmatically dismiss an alert. Whatever is causing it to disappear is most likely triggering the display again.
To debug I suggest:
(1) Looking in your code for the NSAlert – dismissWithClickedButtonIndex:animated: method and see if somehow you're actually dismissing the alert programmatically.
(2) I believe (someone double-check me on this) that an alert view is added as a subview to whichever base view is currently on screen. It might be that the base view is disappearing for some reason and taking the alert view with it. If the view disappears and then reappears rapidly enough, it might not be obvious when the alert is frontmost. (Edit: see Ed Marty's comment below.)
(3) Since this happens in two separate pieces of the app, compare both to find a common element or structure. That common element might be the cause.
An odd problem.
Edit01: Updated for additional info
If locMan isan instance variable, it should be defined as a property and you should access it every time withself.locMan By accessing it directly, you lose your automatic retention management.
I encountered the same exact issue with the alert dialog appearing momentarily, reappearing, and finally appearing again after being dismissed. I was making a string comparison before deciding to show the alert view:
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if([string isEqualToString:#"OK"]) {
NSLog(#"(Settings)Registration Successful");
statusField.text = #"Registration successful!";
[settingsActivity stopAnimating];
}
else {
NSLog(#"(Settings)Registration Failure");
[settingsActivity stopAnimating];
UIAlertView * regFail = [[[UIAlertView alloc] initWithTitle:#"Registration Error!" message:#"Please check your email address and try again." delegate:self cancelButtonTitle:#"OK" otherButtonTitles: nil] autorelease];
[regFail show];
}}
To correct this behavior I simply verified the returned string rather than just showing the alert:
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if([string isEqualToString:#"OK"]) {
NSLog(#"(Settings)Registration Successful");
statusField.text = #"Registration successful!";
[settingsActivity stopAnimating];
}
else if([string isEqualToString:#"Error"]) {
NSLog(#"(Settings)Registration Failure");
[settingsActivity stopAnimating];
UIAlertView * regFail = [[[UIAlertView alloc] initWithTitle:#"Registration Error!" message:#"Please check your email address and try again." delegate:self cancelButtonTitle:#"OK" otherButtonTitles: nil] autorelease];
[regFail show];
}
I also got the Same problem while working on Location Manager. Here i checked with Nslog but it is executing multiple times, finally i fount that i am creating multiple objects and using Sharedinstance for same ViewController that contains Location Manger but i am not releasing the object, so at perticular location how many objects if we create that many times the location detects.So while working on LocationManger check handling objects thoroughly to reduce these type of problems.

What should be the reason behind this leaky line?

I have follwowing peace of code in which I have specified the leaky line . As I am new to iPhone developement I can't understand what actually is wrong with that line . please have a look at that line and tell me .
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
//take away
//NSURL *url1 = [[NSURL alloc] initWithString:#"http://url/Service.asmx/takeAwayList"];
NSURL *url1 = [[NSURL alloc] initWithString:[NSString stringWithFormat:#"%#/Service.asmx/takeAwayList",serviceURL]];
NSMutableURLRequest* request1=[NSMutableURLRequest requestWithURL:url1];
[request1 setHTTPMethod:#"POST"];
[request1 setTimeoutInterval:10];
//*****the leaky line***********************///
NSData *data2=[[NSURLConnection sendSynchronousRequest:request1 returningResponse:nil error:nil] autorelease];
if(data2 == nil)
{
UIAlertView* alert = [[UIAlertView alloc]initWithTitle:#"Alert" message:#"The network is not available.\n Please check the Internet connection." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
[alert release];
}
else
{
NSXMLParser *xmlParser1 = [[NSXMLParser alloc] initWithData:data2];
//Initialize the delegate.
TakeAwayParser *takeAwayParser = [[TakeAwayParser alloc] initTakeAwayParser];
//Set delegate
[xmlParser1 setDelegate:takeAwayParser];
//Start parsing the XML file.
#try {
BOOL success = [xmlParser1 parse];
if(success)
NSLog(#"No Errors");
else
NSLog(#"Error Error Error!!!");
}
#catch (NSException * e) {
NSLog(#"Exception in parsing %# %#",[e name], [e reason]);
}
[takeAwayParser release];
[xmlParser1 release];
}
//[request1 release];
// [response1 release];
//
[url1 release];
// [data2 release];
//new arrivals
//[data2 release];
[pool release];
I had issues with this as well in my Large project. After working with an Apple engineer on trying to locate the leaks, he finally asked the main Apple dev team behind NSURLConnection. They basically said that there is an internal cache that is not clearable at all in NSURLConnection and it was a known issue.
So I set out looking for alternatives. I found ASIHTTPConnection (link below) which works off of CFNetwork. It is designed to be a drop-in replacement for NSURLConnection, plus a bunch of other awesome goodies like downloading to disk instead of memory, download resuming, progress bar callbacks etc..
I have used it in all my projects and have never had any issues or complaints. An, in answer to your question, this is how I got rid of those memory leaks.
http://allseeing-i.com/ASIHTTPRequest/
This line isn't leaking, you shouldn't even being autoreleasing it.
Do yourself a favor and read the Memory Management Guide in Apple's developer docs, commented out releases in your code do not bode well.
EDIT: Hrm I take that back your code is completely fine except for that one line. Are you sure that it's leaking? It's returning an object with a retain count of 0, so you autoreleasing it should be causing trouble because it already has a retain count of 0.