How to implement didReceiveMemoryWarning in Swift? - ios

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

Related

What level of memory usage will trigger iOS's memory warning?

My app may consume tens of MB of memory, and in rare cases it reaches 100MB. Do I need to worry about memory warnings and implement didReceiveMemoryWarning()? And how much time do I have to release memory? (I need to persist the data in memory to the hard drive.)
Suppose I target devices after iPhone 5.
First off, here's the discussion of the method didReceiveMemoryWarning from Apple docs.
DISUCSSION
Your app never calls this method directly. Instead, this method is
called when the system determines that the amount of available memory
is low. You can override this method to release any additional memory
used by your view controller. If you do, your implementation of this
method must call the super implementation at some point.
As per this reddit thread, even if you handle this event, your application can still be terminated to give space to the running applications. Also, often times all of the apps' didReceiveMemoryWarning on the device are invoked, not only yours.
I hope this answer is okay since this seems just a comment quoting the documentation :)

What is considered normal behavior for iOS app memory usage?

I'm developing an application using Xamarin.iOS. It is common knowledge, that its memory is handled very inefficiently and leaks are almost inevitable. That's why I'm trying to be as careful as possible and not to do anything stupid: no circular dependencies, no event listeners left unremoved, etc. I'm using Instruments to monitor resources used by my app. I have a following app design:
There is a UIViewControllerA which has a UITableView and a couple of labels/buttons. When you press on any of three table entries, you're navigated to UIViewControllerB, UIViewControllerC, UIViewControllerD respectively. It also makes a couple of HTTP requests. Each of the controllers also have labels, graphics, tables, scrollviews, buttons etc, and they also make HTTP requests. I'm testing my app by going from UIViewControllerA to those three and back, randomly. Meanwhile, I can see Real Memory column of Instrument's Resource Monitor increase by 1-2MB with every new appearance of new UIViewControllers. It never goes down. Since I don't really know, how memory should behave and since there is virtually no information on that topic, I don't know if this is considered to be a normal behavior for an iOS app. Is it okay? I do realize that my question is very vague, but I need to know if I need to pay attention to those "signs".
Without deeper knowledge of your code and what exactly it is doing, I would say it is not a normal behavior. If you switch between ControllerA, B and back to A it should also free up some memory. A good memory usage should look like a sawblade with up and downs.
The general problem is that the GC may not free up the memory immediately. If you have memory-heavy operations like image-processing you could consider to make use of GC.Collect() - but that should be your last option - to force a collection and free up some memory.
In addition to Instruments, which I also use heavily to profile my apps, is the Xamarin Profiler to gain more insights of what is still alive and keeps some references. If for some reason you can not use Xamarin Profiler use the build in tool called Heap Shot.

Does iOS cache images by default in memory? (RAM)

I've been struggling to find out why my application continues to increase in memory as I move throughout the application.
When leaving the view, I make sure to check to see if the controller is de-initialized and it is, but the memory that was added while in the view retains. I've used the instruments tool and it hasn't detected any leaks, and leaving/re-entering the view repeatedly doesn't have any effect on the used memory.
This leads me to believe that iOS by default caches the UIImage into memory, and only frees the memory if the device needs it.
The view that I'm working with is a UICollectionView which shows the user a gallery of pictures that have been uploaded to my server. Currently I have this limited at 10 images per user, but as you can imagine if there's quite a few images and that can increase the memory rather quickly.
Do I need to worry about this memory? Is it default behaviour for the images to stay in memory until the device needs to free some space? I don't want to submit to the application store and get rejected for poor memory-management.
EDIT: It's also fair to note that I am constructing the image using the UIImage(data: NSData) constructor.
iOS does natively cache plenty of memory. The underlying support for that is in libcache, which UIKit uses internally, in a way that is inaccessible to you.
During times of "memory pressure", that is, when RAM is low, an event (technically , a knote) is broadcast to all listeners. Your app is listening because the frameworks automatically open a kevent file descriptor, and react to it with the well known didReceiveLowMemoryWarning.
Remember, there's no swap in iOS (discounting compressed RAM for the scope of this answer), so this happens quite frequently.
Even before didReceiveLowMemoryWarning is passed to you, libcache uses the malloc feature of "zone pressure relief" , which you can see for yourself in :
...
/* Empty out caches in the face of memory pressure. The callback may be NULL. Present in version >=
8. */
size_t (*pressure_relief)(struct _malloc_zone_t *zone, size_t goal);
} malloc_zone_t;
Thus, images (main consumers of memory) will be purged if necessary, and therefore should be of much concern to you. That said, if you know you don't want a given UI object anymore, you can of course dispose of it explicitly. Of course, if you have any additional resources that cannot be auto-purged in this way, you should handle them in the delegate. Because if you don't, Jetsam will jettison your app (i.e. kill you with an untrappable -9), and probably slay a few innocents in your priority band as well.

possible to leak in-memory data from one ios app to another?

Preface: I'm not an iOS developer and know next to nothing about the iOS security model. So forgive me if this question is truly dumb. :)
Consider an app, called MyApp, that does the following when launched:
Dynamically allocates a chunk of memory, say using malloc().
Loads some sensitive data over the network and stores it in that chunk of memory.
Sits there doing nothing.
Now consider the following scenario:
User launches MyApp.
User closes MyApp.
User launches SomeOtherApp.
My question: If SomeOtherApp also dynamically allocates memory is it possible that one of the buffers returned by the OS will contain the sensitive data placed there by the (now closed) invocation of MyApp?
Or are the contents of RAM treated as part of the sandbox in which an app runs?
Theoretically once the user closes the app (you have to make sure that the app is closed and not just running in the background) the memory that was allocated to that process is deallocated and returned.
To quote from a tutorial on ARC:
"With Automatic Reference Counting enabled, the compiler will automatically insert retain, release and autorelease in the correct places in your program. You no longer have to worry about any of this, because the compiler does it for you."
So when an app closes, all references to any objects which had some sort of memory allocation will be cleared because there will be no objects to reference when the app is not running.
The reason that I say that you have to make sure that it is closed is because some apps, by default, will not close when you press the home button, but will in fact continue to run in the background. This might cause a potential security threat, but unlikely. To ensure that NO memory is still being held on to by that app, make sure that it actually fully closes each time. Make sure that the code is done right and that the person who is writing it, knows and is keeping track of the memory that he allocates.
If security is a big issue, then make sure that all of the memory that does get allocated get properly deallocated in your code. Then make sure that you do an insane amount of testing for memory leaks and whatnot to make sure that no object is left lying with some amount of memory.
I would just like to say that I am not a professional on memory management of even ARC, so it would be best to check with a couple other sources to ensure that my answer is correct.
If I have spoken in err, someone hit me.

iOS : ARC, not freeing memory

I've kind of a weird issue with my iOS app.
after a while my app goes low in memory so memory warning, everything seems to be fine, but when I check the memory usage I noticed that all the calls to viewDidUnload didn't free up lot of memory, so after a few click in my app, it goes again in memory warning, everything seems to be fine again, but not a lot a memory have been released, so it goes again in memory warning faster, and then it crash (after the third memory warning most of the time). this crash is random : app freeze, app leaves, my debugger says app paused, but no bad access or sigbort, no zombies.
my guess is that memory warning can't free up enough memory has it should.
(I checked all my viewDidUnload and make nil every objects that are allocated in viewDidLoad)
Any help will be usefull !
thanks a lot.
So I managed to work with my issue.
I wrote "-(void) dealloc" methode in all my controllers and check if I enter in it as I should. (on pop controller, dissmiss etc..)
Every time it didn't, I do step by step in the controller to see what was retaining my controller from beeing dealloc.
most of the time it was some property that was not in "unsafe_unretained"
delegate that was in "ASSIGN" (and should not be in assign but in unsafe_unretained)
(heritage from non-ARC project...)
I also had some strange controller with XIB that was not deallocated even if empty.
I rebuild new one step by step with copy/paste and finaly with exactly the same code, the new controller was released, with no visible difference between then !!! gnneee
at least I know how to debug that kind issues now...
I don't think there's any way to give a specific answer without more data so the best I can do is suggest that you stop guessing what might be happening with your app and learn how to measure what is actually going on. Run your app under Instruments and you'll be able to check for leaks and also actually see what classes are responsible for the most of your application's memory footprint.
You should make sure you know how to use both the Leaks instrument to identify leaked object but also the Allocations instrument to identify orphaned (but not leaked) sets of objects which should have been released or just cases where your app is not responding to memory warnings as you expected.
https://developer.apple.com/library/ios/#documentation/developertools/conceptual/InstrumentsUserGuide/AboutTracing/AboutTracing.html might be a good place to start and there are a number of tutorials available as well; http://www.raywenderlich.com/2696/how-to-debug-memory-leaks-with-xcode-and-instruments-tutorial and http://www.friday.com/bbum/2010/10/17/when-is-a-leak-not-a-leak-using-heapshot-analysis-to-find-undesirable-memory-growth/ are among the first results I saw.
Vassily,
First, if you aren't yourself releasing extra memory, the the -didReceiveMemory warning does you no good and the OS will keep asking for memory until you are killed. This sounds like it is your problem.
Second, if that isn't the problem then you are probably getting terminated due to the size of your resident memory partitions. Make sure you look at your VM allocation in Instruments. I expect the MALLOC_TINY or MALLOC_SMALL both have greater than 5 MB resident and dirty footprints. Due to the nature of small allocations these VM regions will never shrink. The only option you really have is to not create a lot of small items in the first place. This is really only something you can address by changing you code's algorithms to use less memory.
Andrew

Resources