Cocos2d 2.0: when is good practice to "purge" caches? - ios

Is it good practice to call the following "purge" methods at the beginning of each Scene?
And if not when should I call them and is there any tutorial in explaining when to use each call? Am I missing something?
[CCTextureCache purgeSharedTextureCache];
[CCSpriteFrameCache purgeSharedSpriteFrameCache];
[CCAnimationCache purgeSharedAnimationCache];
[CCShaderCache purgeSharedShaderCache];
[[CCFileUtils sharedFileUtils] purgeCachedEntries];
(I am using Cocos2d 2.0 and ARC is enabled, don't think it is relevant but still thought that was worth mentioning)

IMO it's bad practice to purge cocos2d's caches at all! Purging caches is as useful a tool as a hammer is in repairing electronic devices.
It's contraproductive to purge everything only to have the characters reload their purged animations and textures in the next frame because they frickin' need them! If you purge caches unconditionally, you haven't done your asset memory-management job (well).
There can still be uses to purge everything but they should be few and far between (like shutting down cocos2d altogether in a UIKit app) - because why would you ever want to remove all of the cached resources? You remove what you've added, and if that isn't enough, you've done as much as you could.
As an app developer you should be aware of your app's total memory usage and what assets are in use and which aren't. Including the resources handled by cocos2d. Release only those assets you know aren't in use currently and will also not be needed in the near future. It's really that simple, yet it's more work than simply purging caches - which seems to do the job but really is just a terrible thing to do.
One problem with purging caches specifically during memory warnings is that memory warnings may occur when you're currently preloading assets. Now when you purge the caches while you're loading assets, you're shooting yourself in the foot because the already preloaded assets will be removed and then need to be loaded again as soon as they're needed. At worst this can actually cause an unrecoverable memory warning if the reload happens instantly due to the additional memory needed to load assets in the first place (ie textures use 2x memory while loading for a short period of time!).
In most cases purging caches will only delay the memory warning related termination, while adding lag to the game in the meantime. In the remaining cases it will simply make for a bad experience as the game stutters to recover, possibly over a longer period of time.
Cocos2D purges the caches during memory warnings only as a last resort measure, and mainly for developers who won't concern themselves with such nonsense as memory usage. It's a solution that works for taking the first few app development steps, perhaps even for a developer's first app, but is really pretty much useless for any serious/ambitious efforts.
As an ambitious app developer with an eye on quality you should react to memory warnings in a more graceful manner. Meaning you first and foremost work hard to minimize memory warnings altogether (here are some tips) and when they do occur, you need to make sure of two things:
Your app may be terminated very soon - be sure to save the app's state.
You release any memory you are absolutely sure is not needed at this point in time.
Regarding #2: If you're programming close to the edge, you may want to fall back to some sort of "memory safety" mode where you tune down your app's memory usage altogether, for example by displaying fewer sprites, particles, combining the use of two textures, removing certain textures at the expense of additional loading times, etc.
If you can't free up enough memory with step #2 then #1 will happen almost inevitably, regardless of whether you purge cocos2d's caches or not. Purging the "unused" textures of CCTextureCache can help, but since sprite frames retain the texture it usually doesn't do much (or nothing) if you're using texture atlases without first releasing the corresponding sprite frames. Keep that in mind.
The process for handling a memory warning thus is:
Know what assets are in use, and which are merely cached to reduce loading times.
Remove the "not currently needed" sprite frames and then call CCTextureCache's removeUnusedTextures. This has the greatest chance of releasing most of the memory your app is using while not really needing it anymore.
Remove any other extraneous memory you may have allocated in your code but isn't currently in use - or fall back to a "memory safe" mode (however you implement that).
Hope for the best.
Do not purge cocos2d's caches unconditionally! It's not helping, it will probably only make things worse.
Considering all of the cocos2d caches, 99% of the memory will be retained by CCTextureCache. So it's pretty much pointless to purge any of the other caches anyway, just ignore them.
You really only need to be looking at which texture atlas you're currently using, and those you don't you remove the sprite frames and their textures.
If you're using .pvr.ccz textures to begin with, you can even ignore "caching to reduce load times" altogether and remove from memory every texture atlas whenever you stop using it - because .pvr.ccz load so fast it makes barely any difference as far as switching scenes is concerned. That also helps to avoid memory warnings in the first place.

Related

Some residue memory never released with ARC?

I have done my best to make sure that views are released when they are no longer needed and when pushing the app and then looking at the Debug Memory Graph I see that no objects are present that shouldn't be there. However when I look at the memory graph I can see that although the graph does sometimes bump back down, it still slowly trends upwards. An example is attached.
I am new to building large projects in iOS and am mostly asking is this normal? Is it simply impossible to free all used memory with ARC? Or is there still more debugging to be done here?
(app starts at ~15MB, ends up at 20.1MB)
Starting at 15 megabytes and levelling off at 20 megabytes is great. This app is tiny, and its memory usage levels off quickly. Problems arise when memory usage keeps rising forever, typically on the level of giga bytes, i.e. many orders of magnitude bigger! So, don't worry, be happy.

How to implement didReceiveMemoryWarning in Swift?

Whenever I create a new View Controller subclass, Xcode automatically adds the method
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated
}
Usually I just delete it or ignore it. This is what all the tutorials I have seen do, too. But I assume that since Xcode gives it to me every time, it should be somewhat important, right? What should I be doing here? I assume that disposing of resources means setting them to nil, but what exactly are "resources that can be recreated"?
I have seen these questions:
How to implement didReceiveMemoryWarning?
UIViewController's didReceiveMemoryWarning in ARC environment
iPhone Development - Simulate Memory Warning
But they are all pre-Swift. Although I don't know much about Objective-C, I have heard that the memory management is different. How does that affect what I should do in didReceiveMemoryWarning?
Other notes:
I am fuzzily aware of Automatic Reference Counting and lazy instantiation
The documentation on didReceiveMemoryWarning that I found was rather brief.
Swift
Swift uses ARC just like Objective-C does (source to Apple Docs). The same kind of rules apply for freeing memory, remove all of the references to an object and it will be deallocated.
How to free memory
I assume that disposing of resources means setting them to nil, but what exactly are "resources that can be recreated"?
"resources that can be recreated" really depends on your application.
Examples
Say you are a social media app that deals with a lot of pictures. You want a snappy user interface so you cache the next 20 pictures in memory to make scrolling fast. Those images are always saved on the local file system.
Images can take up a lot of memory
You don't need those images in memory. If the app is low on memory, taking an extra second to load the image from a file is fine.
You could entirely dump the image cache when you receive that memory warning.
This will free up memory that the system needs
You are creating an amazing game that has a number of different levels.
Loading a level into your fancy game engine takes a while so if the user has enough memory you can load level 3 while they are playing level 2.
Levels take up a lot of memory
You don't NEED the next level in memory. They are nice to have but not essential.
LevelCache.sharedCache().nextLevel = nil frees up all that memory
What shouldn't you deallocate
Never deallocate the stuff that is on screen. I've seen some answers to related questions deallocate the view of the UIViewController. If you remove everything from the screen you might at well crash (in my opinion).
Examples
If the user has a document open that they are editing, DON'T deallocate it. Users will get exceptional angry at you if your app deletes their work without ever being saved. (In fact, you should probably have some emergency save mechanism for when that happens)
If your user is writing a post for your fabulous social media app, don't let their work go to waste. Save it and try to restore it when they open the app again. Although it's a lot of work to setup I love apps that do this.
Note
Most modern devices rarely run out of memory. The system does a pretty good job of killing apps in the background to free up memory for the app running in the foreground.
You have probably seen an app "open" in the App Switcher yet when you tapped on the app it opened to its initial state. The OS killed the app in the background to free up memory. See State Restoration for info on how to avoid this problem.
If your app is getting consistent memory warnings when you aren't doing a huge amount of processing make sure that you aren't leaking memory first.
Detecting memory leaks is outside the scope of this answer. Docs and a tutorial.
When didReceiveMemoryWarning is called, it means your app is using too much memory (compare with memory of device), and you should release any additional memory used by your view controller to reduce the memory of your app. If the memory app gets over the memory of device, iOS will kill your app immediately. "resources that can be recreated" means somethings you can recreate it again at somewhere, you don't need them now (don't need to put them in the memory). And you can release it when get didReceiveMemoryWarning.
Here is another detail topic: ios app maximum memory budget

Air for iOS - how to handle crashes due to limited memory?

We're currently developing an iPad application using Air for iOS and from time to time experience crashes (on iPad1 with ios 5 only) which seem to be because the application is using up too much memory.
How to catch/handle such errors in the application? how to be notified when memory is low? trying to catch flash.errors.MemoryError doesn't seem to work. Any tips?
I've done some work in this area and here are some tips that I can give you.
Get Flash Builder 4.6 Premium.
Get it if only for the profiler alone. It has one of the best profilers available for diagnosing things like this. With that said, there are other Flash profilers around, that have varying degrees of usefulness.
This alone will help you find and diagnose where most of your memory is going in terms of raw memory usage, but also help you find how many objects you are creating and destroying and how long they are hanging around before the garbage collector is finally getting around to letting them go.
Pool smaller trivial objects
Rather than constantaly creating and destorying smaller objects, create object pools. This will save you the cost of spinning up new objects constantly, and keep you from having to wait until the garbage collector to run before releasing the memory.
There are a lot of examples and patterns to look at for creating object pools in actionscript. It would be easier if AS supported generics, but even without them its still pretty straight forward.
Eagerly dispose of huge objects
This goes directly against the advice in the previous point, but for huge objects, you don't want them hanging around in memory forever. I'm referring to things like BitmapData, when you are done with them (for the foreseeable future), tear them down and null them out, and let the garbage collector clean it up.
When you need them again, rebuild them. Yes, you will take a slight performance hit, but memory on mobile devices is precious and don't waste it by keeping around a 2mb bitmapdata object that only ever appears on the loading screen. Throw it away.
Null out references you don't need anymore
Take some time and try to really understand what the garbage collector needs to do its work, and how its decides which objects can and cannot be thrown away. Try to avoid self referential objects/circular references, while the CG can normally figure it out, sometimes it might need a litle hand holding.
Evaluate every time you use new [Related to 2]
Again using a memory profiler will help for this step, but make sure that every time you instantiate a new object, you need to instantiate a new object. It can be very easy to get lazy when developing for a PC, just throwing new objects into the pool and letting the CG sort it out. See if there are good caching strategies (object pooling, or just reference caching) if its small. And if its a HUGE object that you are building up and tearing down often, it might be time to try to come up with a better architectural solution.
As far as I know, if you get to the point where iOS thinks the memory is low, its already too late. Last time I checked, the framework will try to run the CG when it thinks its running out of memory, and if it can't free up enough memory to continue, it fails out. Do your best to try to avoid getting to the point where the operating system thinks the only safe option is to terminate your thread.

iPhone memory warnings and crashes - but Instruments showing lowish memory use

I have a strange memory issue I'm having problems resolving and would appreciate some advice as to where else to look.
The program I have (iPhone App) has a function whereby it basically downloads loads of files, processes those that are JSON, and stores the rest to disk. The JSON processing is CPU intensive and can take several seconds per file, so I have a NSOperationQueue with maxConcurrency limited to 1 that handles all the heavy lifting, and a queue that manages the multiple files to download.
Ever since iOS5 came out, the App has had problems completing the download sequence without crashing and so far what I have tried is;
1) Changed the performSelectorOnBackgroundThread JSON processing to use a single NSOperationQueue so as to limit the number of background threads working with large objects.
2) Added NSAutoReleasePools inside loops that create multiple, large, transient objects.
3) Flushed the sharedURLCache to ensure the files aren't hanging around in the system cache.
4) Stored the JSON objects to disk using NSKeyedArchiver and passed the filenames between threads rather than the actual objects, to again try to mitigate the number and size of retained objects currently in use.
All of these at first seemed to make a difference, and when I look at the memory allocations, I've now got the peak usage down from just over 20MB (hence no wonder it was crashing) to under 10MB, and yet the app is still crashing with low memory as before.
I'm trying to trace what is eating the memory causing the app to crash and on this occasion I'm having real problems persuading Instruments to tell me anything useful.
Here's a typical trace (on an iPhone 3GS running iOS 4.3.5)
You can see that the PEAK usage was a tad over 7MB and yet shortly after, you can see the 2 flags pertaining to low memory, and then low memory urgent, followed by the app terminating shortly thereafter.
If I use the memory monitor, the cause of the crash seems clear enough - physical memory is being exhausted - look at the light green trace below. The low memory warnings co-incide (not surprisingly) with the physical memory running out.
There are no leaks showing FWIW either (I've done that in other runs).
It's not image caches or NSURLConnection caches and the only thing I can think of is that perhaps there are some bad leaks that aren't being detected ... but I'm having issues identifying them because if I click into all allocations to see the objects that are live, and then do a command-A to select them all (in order to paste them into a spreadsheet to see where the memory seems to be), at the point I hit command-C to copy them, Instruments beachballs and never recovers.
I really cant figure out what's going on. Does anyone have some advice on how to persuade instruments to show me some more useful information about what is using this memory?
Sorry I can't post any meaningful code fragments ... hopefully the instruments screenshots at least give you an idea about where I'm coming from.
The Leaks instrument isn't terribly useful for figuring out anything but the obvious leaks in your app.
What you are describing is an ideal candidate for heapshot analysis.
tl;dr Heapshot analysis allows you to see exactly how the heap of your application grows between any two points of time (where you determine the points).

Windows Task Manager shows process memory keeps growing even though there are no memory leaks

My application keeps consuming more and more memory as seen in the Windows Task Manager and eventually crashes due to OutOfMemory. However when i check for leaks using MemoryValidator (from www.softwareverify.com) no leaks are detected. Why is this happening?
Just because there is a growing amount of memory usage doesn't mean it is necessarily 'leaking'. You could simply be accumulating a large number of live objects and/or very large ones (containing lots and lots of data).
If you can provide more information about what language(s) you are using and what the application is doing I can perhaps help out with some more specific information!
UPDATE AS PER COMMENTS
Well, you'll just want to make sure the garbage collection is happening correctly. I'd suggest the libgc library to help with that perhaps.
http://developers.sun.com/solaris/articles/libgc.html
The only other thing I could think of as being the cause of this is that you are maintaining references to the objects somewhere unintentionally so they are just piling up.

Resources