I have an iPad application that builds and runs perfectly the first time. But if I exit the application and open it again, the interface is completely unresponsive. If I exit and open another time, it never gets past the splash screen.
What's strange is that if I wait a minute or two before opening it again, it always runs fine.
Any ideas on what might be going on or where I should start my debugging efforts? I would throw in breakpoints and see what's going on, but by the time I start the application a second time, the debugger has already exited. Is there a way to keep the debugger and console running through multiple executions of an app?
Thanks,
Luke
Edit: Here's some code I use for NSUserDefaults - could this be the problem?
In viewDidLoad in my main view controller:
bgnum = [prefs integerForKey:#"bgnum"];
menuVisible = [prefs boolForKey:#"menuVisible"];
songTitles = [[NSMutableArray alloc] initWithArray:[prefs arrayForKey:#"songTitles"]];
numberOfSongs = [prefs integerForKey:#"numberOfSongs"];
In viewWillDisappear:
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
[prefs setBool:menuVisible forKey:#"menuVisible"];
[prefs setInteger:bgnum forKey:#"bgnum"];
[prefs setObject:songTitles forKey:#"songTitles"];
[prefs setInteger:numberOfSongs forKey:#"numberOfSongs"];
[prefs synchronize];
I'd look at what's happening in your initialization code, loadView, didFinishLaunching, etc., and trace it out. It's easy to get 3 or 4 methods deep in that stuff and do too much there when some of it should be lazy. With out more information or any sample code it's all wild guesses. One such guess is user defaults or anything that you're loading from a file or dictionary up front? I could see how something there could cycle through several states.
There's a big difference between "exiting the application and opening it again" which in context implies you are doing it rather quickly and "waiting a minute or two".
I have a rather large application that I develop on which routinely takes several seconds to "unload" on the iPad, which results in me rapidly exiting and re-entering causing the application to appear in a partially configured state, and causing unusual behavior.
This could be due to the fact that your application hasn't completely dealloc'd and closed its threads out, and thus when you launch it again quickly, the thread is resumed (and on 3.2 or earlier, will be unstable since it's already started killing itself off).
Do some timings... see what the threshold is for "it crashes" and "it works". If it's more than 10 seconds, then I would say something is wrong. Less than that, and you could be seeing what I just described,
With the device connected, run the app in debug mode from xCode and then exit the app. Now, disconnect the USB cable and run your app again - is it completely frozen? If you rotate the device, does your view orientation change? Once the app is terminated, plug your USB back in and go to xCode->Window->Organizer.
You should see your iPad device on the left of the screen. Select it and you should see some tabs on the right for Console and Crash report. Select your app from the dropdown and look to see if any console messages have been logged, or if a crash has occured - you should get a stack trace if it did which should help.
I once had a similar problem when I had NSZombieEnabled as an active argument in my executable, so that could be worth investigating too.
Related
I found it took 4-5 seconds on an iPhone4/iOS6.0 device, and I want to know what is the system doing in that time, are there any best practices to short the launch time of an app?
many thanks.
EDIT:
I log it and it took about 1.5s in applicationDidFinishLaunchingWithoptions:
,and it is like 3.5-4s(I used a clock to get a preciser time) from I tap the app icon to its launch screen disappear.That is to say, about 2-2.5s to launch the app before calling applicationDidFinishLaunchingWithoptions
Simply watch Session 225 Up and Running: Making a Great Impression with Every Launch from WWDC 2012, it will take you through the process of profiling your app launch and give tips on most often made mistakes, it's great.
There is a very very good WWDC 2012 video with a long sequence devoted to exactly this topic: Session 235 (Responsiveness). I strongly recommend that you watch it. It gives you lots of hints and tricks for speeding up launch (don't link to too many libraries, etc.), tells you how to measure and how to read your measurements in Instruments, etc.
The system isn't what's causing your app to take 4-5 seconds to launch. It's probably your code in applicationDidFinishLaunching, or the init, viewDidLoad, or other setup methods of your root view controller.
Profile your app on a real device with the 'Time Profiler' Instrument. Only launch your app, don't press any buttons on the UI once it's launched. We only want to see what's going on during launch.
I would suggest checking the 'Separate by Thread,' 'Invert Call Tree,' 'Hide System Libraries,' and 'Show Obj-C Only' options under 'Call Tree' on the lower left side of the window.
Once you've checked your chosen options, go down the list of method calls. Start with the ones taking the most time (for obvious reasons). You can double click on symbol names to get more detailed information on where time was spent. Good luck!
We had the same problem.
Ultimately, we sped up launch time significantly by preloading the video in viewDidAppear. By the time the user taps the play button to launch the movie, enough content has loaded so that it can begin playback almost immediately.
e.g.:
- (void)viewDidAppear:(BOOL)animated {
NSURL *movieURL = [NSURL URLWithString:#"http://static.example.com/movie.mov"];
MPMoviePlayerViewController *vc = [[MPMoviePlayerViewController alloc] initWithContentURL:movieURL];
vc.moviePlayer.shouldAutoplay = NO;
[vc.moviePlayer prepareToPlay];
self.moviePlayerViewController = vc;
}
Full details in this blog post: http://blog.tetherpad.com/blog/2013/3/22/improving-tutorial-movie-load-times-on-ios-for-fun-and-profit
In my opinion, the launch time mainly cost in function,
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;
If you do a bunch of stuff which blocks process, it will take long for launch.
So you can optimise your applicationDidFinishLaunching by moving out some unnecessary code which you can do later. Or you can make your code non-block, for example, you should use asynchronous network requests in applicationDidFinishLaunching instead of synchronous ones.
Hello I am planning to develop a simple iPhone game. I would like to be able to distinguish between a genuine crash, and the user killing the app ( by double tapping home screen and long-clicking it to kill it) .
Can someone please shed some light on what exactly happens when the user kill the app through the multitasking bar.
If your app is in the background and suspended when the user kills it, it will receive no notification. This accounts for the majority of cases.
If your app is currently running in the background (there are only very specific categories of apps that can do that), then it receives applicationWillTerminate.
Indeed, Apple is very clear as to the fact that you should save any relevant data before entering the background. Have a look at this (chapter "Responding to Application Termination"):
Even if you develop your application using iOS SDK 4 and later, you must still be prepared for your application to be killed without any notification. The user can kill applications explicitly using the multitasking UI. In addition, if memory becomes constrained, the system might remove applications from memory to make more room. If your application is currently suspended, the system kills your application and removes it from memory without any notice. However, if your application is currently running in the background state (in other words, not suspended), the system calls the applicationWillTerminate: method of your application delegate. Your application cannot request additional background execution time from this method.
EDIT:
about the "saying sorry" thing...
you can certainly do that on the next launch. simply store a key in NSUserDefaults and remove it when the app enters the background (I hope all this sounds familiar to you, otherwise look into the UIApplicationDelegate protocol).
when the app starts up, you check the key; if it is there, then the app was not closed by the user; if the app is not there, then the user at least moved the app to the background and did not experience any sudden termination...
For iOS6 and later there is a way to do this. A side effect of State Restoration is that it will delete the state when there is either a crash during restore or a user manually kills the app. You can use this to your advantage to detect a user manually killing the app.
From the documentation:
Be aware that the system automatically deletes an app’s preserved state when the user force quits the app. Deleting the preserved state information when the app is killed is a safety precaution. (The system also deletes preserved state if the app crashes at launch time as a similar safety precaution.) If you want to test your app’s ability to restore its state, you should not use the multitasking bar to kill the app during debugging. Instead, use Xcode to kill the app or kill the app programmatically by installing a temporary command or gesture to call exit on demand.
The following code assumes that you already have a BOOL for crash detection called _didCrashInLastSession. There are different approaches for getting this value such as this 3rd party library. In your code call the method [self getLaunchType] to see which type of launch you are dealing with and act on that accordingly. Put the following in your AppDelegate.m:
typedef NS_ENUM(NSInteger, LaunchType) {
LaunchTypeUnknown,
LaunchTypeNewInstall,
LaunchTypeNormalLaunch,
LaunchTypeCrashedLastSession,
LaunchTypeUserManualQuit,
};
static BOOL hadStateToRestore = NO;
static NSString * const kAppHasEverRunKey = #"appHasEverRun";
- (BOOL)application:(UIApplication *)application shouldSaveApplicationState:(NSCoder *)coder
{
// Called when going into the background
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:kAppHasEverRunKey];
[[NSUserDefaults standardUserDefaults] synchronize];
return YES;
}
- (BOOL)application:(UIApplication *)application shouldRestoreApplicationState:(NSCoder *)coder
{
// Called on start up
hadStateToRestore = YES;
return YES;
}
- (LaunchType)getLaunchType
{
if (_didCrashInLastSession) {
return LaunchTypeCrashedLastSession;
}
if (![[NSUserDefaults standardUserDefaults] boolForKey:kAppHasEverRunKey]) {
return LaunchTypeNewInstall;
}
if (!hadStateToRestore) {
return LaunchTypeUserManualQuit;
}
return LaunchTypeNormalLaunch;
}
Update: At least one 3rd party SDK breaks this technique: Urban Airship.
You can do it through your device also.
Connect your device to your machine.
Run xcode and go to organizer.
There select your device and device logs.
There you can also see crash logs of your app or game.
I had uploaded my app to the App Store but it was rejected two times, with the reason being that the app is crashing on iPhone 4 (iOS 4.3.5). I have tested my app on the same version but I can't make it crash.
My app is getting user location for drawing a path on g-map. I have also tested with location service enable/disable, wifi on/of. For all these ways I have tested, my app is not crashing.
After symbolicating my last crash report I have found location and that is in cellForRowAtindexpath:
if([finalarray count]>0)
{
cell.shopnamelbl.text=[[finalarray objectAtIndex:indexPath.row] valueForKey:#"name"];
cell.distancelbl.text=[NSString stringWithFormat:#"%#km",[[finalarray objectAtIndex:indexPath.row] valueForKey:#"distance"]];
}
else if([unsortedarray count]>0)
{
cell.shopnamelbl.text=[[unsortedarray objectAtIndex:indexPath.row] valueForKey:#"name"];
cell.distancelbl.text=#"";
}
else
{
cell.shopnamelbl.text=#"Loading...";
cell.distancelbl.text=#"";
}
return cell;
in above code on cell.shopnamelbl.text=#"Loading..."; this line is the cause of my crash, but in my testing everything works perfectly. I am not seeing a crash anywhere else in my app.
So now what can I do to fix this?
There's nothing obviously wrong with the code you've posted, so it's likely to be some other difference between what you've been testing and what Apple have on their handsets.
On first run, their device will have nothing in NSUserDefaults set, nothing in the keychain, nothing in the Documents directory. You need to completely wipe your device of any trace of previous runs of your app. Deleting and re-installing isn't enough.
It might also be worth trying on a number of devices. It's possible that you've inadvertently made some assumptions about how long certain operations take which don't hold on the faster/slower/different handsets that Apple uses for testing.
How can I gather Instruments memory/zombies data after removing app in simulator and restarting in simulator?
What I see in the simulator after I remove the app process, and then retart it by clicking on it's icon in the simulator, is that Instruments has stopped receiving the data from the application?
Background
I'm trying to test saving and loading data via NSUserDefaults. After saving it to test the loading part I need to simulate removing the app from memory, so what I have been doing is manually removing the app process on the simulator (double click on menu button etc).
What I'm finding is that when I do run up the app this way there is some problem at start up - getting "Thread 1 - Program received signal SIGKILL" against the " int retVal = UIApplicationMain(argc, argv, nil, nil);" line of code
Therefore I thought I would try to run in Instruments to track down the issue, hence my question as Instruments seems to "stop recording" after I kill the app process on the simulator and then restart.
Not possible (i.e. no answers yet so I'm guessing the correct answer may be that it's not possible)
I have used:
[UIApplication sharedApplication].idleTimerDisabled = YES;
in a number of Apps developed and running under iPhone OS 2.x and never had any problems with it. They were clock apps so needed to run constantly and ignore the iPhone's idle Timer setting.
However, trying to achieve the same with a new App running OS 3.0 (and which needs to be deployed under 3.0 as it uses some 3.0 APIs) I've found the idle Timer to be either ignored or inconsistent.
My App plays music from the iPod library and when the music is playing it auto-locks regardless of the above setting. But once you unlock it, it then doesn't auto-lock again unless you play music again, in which case it locks again after the iPhone auto-lock time setting.
I'm amazed no-one else has come across this as I imagine it would affect a large number of Apps.
Just to clarify:
1. The above code is in ApplicationDidFinishLaunching
2. I know that the phone won't auto-lock when testing from xCode regardless of settings
If anyone has any thoughts I'd be very grateful...
Our app uses the MPMediaPLayer. We also had the idleTimerDisabled=YES code in the ApplicationFinishedLaunching, which works EXCEPT if untethered, and there is already a current nowPlayingItem which is left playing (or unpaused, if paused at app startup). Obviously this is all with the Settings -> General -> Autolock set to some timed value.
By adding idleTimerDisabled=NO, immedately followed by idleTimerDisabled=YES in one of the other bits of code AFTER we had figured out what bit of music we would get playing seemed to solve the problem. Just setting it to YES was insufficient.. and subsequent queries had always indicated the correct value (YES).. so it appears the Apple code ignores the setting of the value IF there is a current piece of music and that is not changed by your code.. but does notice a change of value.
This is all under iOS 3.0.
Even in 2015, using iOS 8.2, this bug is still alive and kicking.
Here's my solution, using XCode 6.2.
iPhone - phone goes to sleep even if idleTimerDisabled is YES
Basically, even now, in 2015, the only way to safely make sure that the device doesn't go to sleep is to repeatedly call a piece of code to keep the device awake.
-(void)callEveryTwentySeconds
{
// DON'T let the device go to sleep during our sync
[[UIApplication sharedApplication] setIdleTimerDisabled:NO];
[[UIApplication sharedApplication] setIdleTimerDisabled:YES];
}
Sounds like a bug, file with Radar - I am not too surprised this has not been seen much as there are probably not a lot of apps that try to lock the screen open and play music.
Having same issue. It does work when the device is plugged in. You can press lock button on top, and my NSTimer fires later and causes a vibrate. However if the device is not plugged in pressing the lock button puts the device to sleep. Any solution would be greatly appreciated.
iCodeblog posted about the idletimer, I said it didn't work, and the person who develops 'cute clock' was nice enough to reply. You have to do a hack, play a 1 second or longer silent sound every 10 or so seconds with NSTimer. This keeps the device awake even if the user hits the lock button.
I develop Seconds - Interval Timer for iPhone and iPod touch and I've had no end of trouble with this. The idea of my app is that people create timers based on a number of intervals where each interval can have it's own playlist or track played.
In iOS3 I had the problem that I couldn't disable the idle timer by just setting idleTimerDisabled = YES. In the end I came up with the same solution as Neil whereby I would periodically set it to NO, then immediately to YES again. This seemed to work.
I'm now updating the app to iOS4 (I know, iOS5 is just around the corner...) and now I have the opposite problem. If the MPMediaPlayer changes track before the idle timer reaches it's limit it gets reset. I've just tested this by creating an interval in my app that was 55 seconds, my auto-lock was set to a minute. At 50 seconds the screen dimmed as it prepared to lock, but at 55 seconds when the music changed it went back to full brightness and then didn't lock as it should.
Overall, the implementation of this seems flakey at best.