I am guessing an iOS App's size has direct impact on its launch latency, since iOS needs to load the App into memory before launching it. If so, I wonder how much is the impact?
Let's say if I reduce my iOS app from 100M to 80M, how much launch latency improvement I can expect?
Does anyone has experience on this?
Consider that programming a faster App Launch experience is not bound to App Size at all. You could assume even the opposite because in some (i would even say in most) cases coding a faster launch process to start up can (and will) involve more code to make it a very short interaction interrupt experience and so your app size would increase without even delivering more valuable content.
In other words just because you deliver more dynamic or static content does not increase launch time unless you load and instanciate everything you need later on in your app life cycle in UIApplicationDelegate instead of allocating objects at the time of its use.
The same ideals apply to tear down processing (dealloc or saving app state user defaults) if needed. But this experience is usually not seen as a visible interruption of interaction with the device and so often forgotten to be part of the apps life cycle.
You can avoid this prolonged start up experience for users with clever use of LaunchScreen storyboards or similar techniques like partly buffered content for the first seconds. Some apps even take screenshots to be presented at the next start up to make the user feel the last app state is loaded already while building up the real interface.
A common mistake, which you may have seen also very often, is to make use of introduction videos or animations to bridge the time in waiting for the window/view to appear after app launch. So just making use of Launch Screens is never the best solution but a wise decision if the content that needs to be loaded first hand will take a noticeably longer time and can not be avoided.
LaunchScreens are just entertaining/informing about processing and keeping the user engaged while it can be beneficial what you and your app want to represent.
So abbreviated launch time is clearly a design pattern issue and is not bound to app size. Ok, apart from downloading band width if that is needed.
Related
I see a lot of posts about people trying to obscure sensitive information from their app snapshot so that its not readable in the multitasking app selector. But I have a different problem.
I'm trying to save as much memory in my app as possible when it enters the background, and one of my app's views has a lot of subviews that is the largest memory usage of my entire app. I've written code to remove and delete these subviews, which i then recreate when the app returns to the foreground (I've actually called these methods in the resign active and did become active methods). Everything is working fine but when I delete these subviews, the view correctly appears rather empty, just in time for the system app snapshot to be taken. So as the app is restored this empty view is displayed for a short time before the app renders properly.
I'd like to be able to manage these views but I guess it needs to happen AFTER the app snapshot has taken place.
Actually profiling the app's memory usage I've noticed that the large (70meg) memory usage that's present whilst the app is in the foreground drawing these views drops right down when the app is backgrounded even if i don't manually delete the views myself. The documentation is clear that the actual backing CA layers etc are all handled by the system so thankfully the majority of memory savings are already being made by the system itself. But still there's another 5-10meg that I can save by doing what I've described here so it feels like a good idea. Is it possible though?
I wonder if I should request to be left running a little longer which I think i saw was possible may be the solution? Especially if the app snapshot is taken before this extra time is given to the app.. This would mean the screenshot is correct and the memory is saved a split second later.
Thanks for your time, Cheers!
I'm not sure why you're worried about 5-10 megs of RAM. The system will reclaim lots of memory by blowing away the backing layers, as you said, and recreating them when the app is foregrounded again. But what, exactly, do you hope to accomplish by reclaiming more memory yourself?
Unfortunately you can't control when the snapshot is taken. You could, I suppose, do a dispatch_after to have your view destruction code happen after a delay, but this wouldn't be deterministic since you don't know when the snapshot will be taken. And it's also possible that the code won't be run at all if your app is asleep when the dispatch timer goes.
I think you may be optimizing prematurely.
Add a snapshot view on top of your view hierarchy before removing the subviews.
UIView *snapshotView = [view snapshotViewAfterScreenUpdates:NO];
Not exactly sure that will help save much of those few MBs you worry about, but that's the common way of doing something like this.
The reason why I am doing everything below: I have a GPS tracking application which should run in background all day long. Memory usage doesn't go down when I put application in background. It seems like views are not removed from memory when app goes into the background... That is why the app is closed in few hours of normal usage of iPhone ... From IOS6 experience - memory usage should drop to cca 8KB.
Then I tried to debug/isolate the problem ...
I created simple ViewController with UIMapKitView just to make sure that view is consuming a lot of RAM (e.g.: 12-17MB). When app goes to background app uses same amount of RAM until "Terminated due to Memory Pressure" message. Still - sometimes memory lowers to 4KB and everything is OK. Problem is because it works kind of "random" and the app is plain simple.
Can I manually release RAM which is consumed by views? E.g.: set self.view = nil? Should I do it?
I also noticed that "- (void)didReceiveMemoryWarning" is not always called before app is killed. I would expect that when OS needs more memory it would first release views, than ask me to clear anything I can via this method ... Isn't this a little bit strange?
Apple redesigned the way a VC's (view controller's) views were loaded quite a while back - I want to say starting with iOS 5? iOS 6? It used to be that the system would unload your VC's views when a VC was not front-most and it needed the memory.
With the changes, a VC's views are NEVER unloaded while the VC is active. ViewDidLoad only gets called once in the lifetime of the VC, and viewDidUnload is no longer called at all.
I don't know what would happen if you set your (non-frontmost) VCs' content views to view manually. Would the system load your view hierarchy when that view became frontmost again? You'd have to try it, but it is risky since I don't think the OS is designed that way any more.
If your VCs views are holding significant amounts of memory, you might want to re-architect your app so only the front-most app is kept around, and the other VCs are released (after saving their state.)
Apple does not make this sort of app easy to write. To the contrary, they actively discourage it. The GPS is a huge power drain, so keeping it active constantly is going to drain the user's battery fast.
I remember hearing about a new location chip in the 5s that will record a "crumb trail" of location data for your app even when it's not running, at your request. You might look into that, although it would only work on the newest devices with the A7 chip and location chip.
In my case the problem was specific IOS7 version. Every app was crashing with this version of OS. When we updated OS version, everything started working back as it should.
There are a couple of games that I want to mention, to show how they handle entering the background mode:
Clash of Clans. It seems it doesn't free any memory at all when it enters background mode, in fact I can open lightweight applications like Notes and get back to the game to continue playing without any noticeable delay, which seems pretty cool to me from the user's perspective.
Game of War. The game immediately falls back to the loading screen and the initialization process starts all over again when it runs inmediately after it enters background mode, it's just like restarting the game, which is very annoying sometimes.
So, in the case of my game, it has an average memory footprint of 25 mb (and it's going to be less with some optimisations), I know the "Free up as much memory as possible" stuff recommended in order to be a good neighbour on the iOS platform, but considering that freeing memory when the game enters background mode could cause my game to have an "annoying" pause when it enters foreground mode...
...should I save the progress and pause the game when entering background mode without freeing up any memory at all, or should I free as much memory as possible and load those resources again when entering foreground mode, with the respective loading pause?
As you yourself point out (by giving two games as examples that use opposite strategies), there is no unique answer to your question.
I think a proper answer can be worked out in a given case by clearly defining the use cases you want your app to support.
E.g., one use case, as you mention, could be "the user can switch to a lightweight app and go back to the game without unnecessary waits". (UC1) Then, you should also have a list of lightweight apps and reference devices where you want to make sure that the use case is actually satisfied.
On the other hand, you may want to support a slightly different use case: "the user will always come back to the point where she left the game, unless the app was terminated, in which case the app should present the main menu". (UC2)
In the first case, not freeing up could be the best approach; in the second case, you may want to minimise the chance that the app is terminated while the user does another task (lightweight or not), so that she can go back to the place where she left, even though she has to wait for all resources to load.
There might even be cases where an intermediate approach is best, where you free up only a part of memory, so to strike a balance.
And, of course, it would make no sense going for the first use case (UC1) if your app memory footprint is so large that it will be terminated almost immediately after switching to another app on most of the devices. In such case, it might make more sense freeing up memory, so you at least can spare the app load time (vs. resources load time) when you go back to it.
So, in the end, it depends...
I would save the progress and pause the game when entering the background without freeing up memory, as many people if they need the memory just delete it from the history thing, or the "carousel of cards". Multi tasking is emphasized in ios 7 so you don't want them to restart the game.
for example, somebody wants to text someone in the middle of playing your game. They exit the game and text someone. But then they have to load the game again. Nobody likes this.
While your game doesn't take up much memory, most users don't bother to close out of applications. I would recommend freeing up as much memory as possible without sending them back to the start menu, much like #sergio recommended with his 'intermediate' option.
I strongly suggest to free up memory. I have an iPhone 4, and some apps are now taking too much persistent memory, so that switching between them becomes a nightmare. E.g. recently, I could not switch between Safari and Twitter anymore, without them reloading all their state (taking more than 20 seconds in both directions).
Bottom line: Try to keep important state, but with as little memory as possible (i.e. not retaining images / textures), otherwise the app will get closed on older devices and you will effectively lose more state.
Save the progress and free the memory with the loading pause, especially if your game is for older devices. I have an iPhone 4, and when a game is running in the background without freeing memory, my foreground apps crash every 15 minutes. But if you have things such as "Vehicle/weapon upgrade is done in x minutes", you will need to save game progress and free the memory, so only the upgrade timer is running. If you do enough optimisations you may decrease the memory footprint, so I would recommend you to keep the optimisations in mind while coding and then the background performance will be your least problem.
I'm at a point in the development of my little app where I'm implementing didReceiveMemoryWarning and such, but I feel like I don't have a good grasp on the best way to test what I'm implementing.
First off, apparently in the simulator, didReceiveMemoryWarning isn't triggered for apps that aren't in the foreground until that app is brought back into the foreground, according to this question - actually, that matches my own experience, but I was prone to ignore it because it doesn't really make sense. (Why would I want to delay clearing up my memory until I'm coming back into the foreground and probably about to need that data again?) Does this match the behavior of actual hardware? If so, does it make sense to cleanup tasks in applicationWillEnterBackground in addition to didReceiveMemoryWarning?
Second, generally, what is a good strategy for triggering the "Simulate Memory Warning" menu item in the simulator in such a way as to trigger memory warnings in a manner that they're likely to happen on the actual hardware?
Q1: Does this match the behavior of actual hardware?
A: Yes, the simulator and the actual device behave the same in this regard (e.g. if your app is suspended and is not running a background task, it does not receive applicationDidReceiveMemoryWarning).
Q2: Does it make sense to cleanup tasks in applicationWillEnterBackground?
A1: Only insofar as that your app must prepare itself for a complete restart. If your app remains suspended and the user does a lot of things with other apps, your app may become fully unloaded - even though the app may still be visible in the task switcher, when the user activates it the next time, your app delegate will receive application:didFinishLaunchingWithOptions: (not applicationWillEnterForeground:).
A2: I wouldn't bother with cleaning up memory, but you should make sure that by the time applicationWillEnterBackground has finished, any user preferences and other stuff are saved so that the app knows where to take up again when it is activated the next time.
Q3: What is a good strategy for triggering the "Simulate Memory Warning" menu item?
A1: I doubt whether a general strategy can be formulated since every app is different.
A2: To use a specific case: In my app, I don't use the app delegate's applicationDidReceiveMemoryWarning to relieve memory pressure, instead I rely on the fact that when a memory warning is sent, iOS also invokes viewDidUnload on certain view controllers that are not in use. Therefore I generally use the memory warning simulation to test whether my view controllers' viewDidLoad and viewDidUnload are balanced correctly.
A3: Concrete example: My app uses tabs, so a favorite scenario of mine is to 1) display the tab with the view controller (VC) under test; 2) navigate to a different tab; 3) simulate the memory warning (iOS invokes viewDidUnload in the VC under test); 4) navigate back the original tab (iOS invokes viewDidLoad in the VC under test).
A4: If you want to use a similar approach, then you have to figure out for your app which views might be unloaded by iOS. In my (limited) experience these tend to be views that are unrelated, such as views on different tabs.
Here's my answer to your second question - it also kind of answers your first question:
I've used the "Simulate Memory Warning" feature to effectively reproduce issues where a child view controller consumes enough memory such that its parent must free up resources.
For example, consider a child view controller that displays the camera/photo library (i.e., UIImagePickerController)). This uses a significant amount of memory. If the picker controller consumes more memory than is available, the parent's view controller will be unloaded, and once the parent view controller is shown again (when the child is popped), the parent's viewDidLoad will be called again. This means that any variables set in viewDidLoad will be set again, potentially with memory leaks or bad pointers.
The "Simulate Memory Warning" helps to find these problems. You can go into the child view controller, select "Simulate Memory Warning", then pop the view controller and check for problems.
Note that the "didReceiveMemoryWarning" is more relevant (imho) to let you know it happened than actually trying to release memory at that point.
I don't have any hardcore data supporting this, but generally, you'll notice that any app you play with or one you're working on, when the app first loads, doing something such as displaying a modal view controller is slow the first time, but every subsequent time, it's really fast. For my app, when I present a modal view controller the first time, it takes about 3 seconds, but if I do it over and over again after that, it takes less than a second every time. What is the reason for this?
There's a bunch of possible explanations here.
Something's been cached. The first time, it had to load something from "disk," the second time it was already in memory. This could be an entire framework, or NIBs or graphic resources in the OS itself.
Memory management. iOS didn't have enough memory to satisfy the request the first time. iOS spent some time clearing out memory, possibly quitting background applications. The second time, it's already available.
Probably many others.
Caching. Off the top of my head, images are often cached, and I wouldn't be surprised if the nib was cached as well.
No, I don't notice this in my app. The cause of what you are seeing could be a hundred different things, so we need a bit more data to make an informed answer.
Suggest you run Instruments, narrow down the time window to the initial 3-second pause, then see what the machine is doing during that time. Run it multiple times and look at CPU, IO, memory, anything that could be slowing it down.