How to Manage Memory for You iOS App

By Jesse Feiler

As powerful as it is, the iPhone — and even the iPad — are limited in resources, and the most critical of these resources for iOS apps is memory. To truly understand how to manage memory correctly in your app, you need to understand how the iOS memory works.

Memory management

Whenever you (or a framework object) create an object using Objective-C, you allocate memory for the object. Although iOS devices and the Mac all use what’s known as virtual memory, unlike the Mac, virtual memory in iOS is limited to the actual amount of physical memory. So when it begins to run low on memory, the Memory Manager frees memory pages that contain read-only content (such as code).

This way, all it has to do is load the “originals” back into memory when they’re needed. In contrast to what the Mac does, iOS doesn’t temporarily store “changeable” memory (such as object data) to the disk to free space and then read the data back later when it’s needed. This state of affairs limits the amount of available memory.

So as you can see, when one object is done using memory, it’s critical that the memory be released for use by other objects.

If memory continues to be limited, the system may also send notifications to the running app, asking it to free additional memory. This is one of the critical events that all apps must respond to.

In Objective-C, memory is managed in iOS apps by reference counting — keeping the system up-to-date on whether an object is currently being used. Read on for all the details.

Reference counting

In fact, memory management is simply an exercise in counting. Every object has its own reference count, or retain count, which is the number of other objects that are currently using the object. As long as the retain count is greater than zero, the memory manager assumes that someone cares about that object and leaves it alone.

When an object’s retain count goes to zero, the memory manager knows that no one needs it anymore and sends the object a dealloc message, and after that, its memory is returned to the system to be reused.

That process sounds pretty straightforward, but how does the retain count get incremented and decremented? Until Xcode 4.2 and iOS 5.0, you had to manage the retain count in your app. When an object is created via alloc or new or through a copy or muteableCopy message, the object’s retain count is set to 1.

When your app uses one of those methods, ownership is transferred to the object that sent the message — that is, the object has been retained and that object that sent the message becomes a nonexclusive owner of the object. Ownership here means that the object will be there to use until it’s explicitly released by sending it a message when it’s no longer needed.

Before Xcode 4.2 and iOS 5.0, if you didn’t create an object by one of those methods but you wanted to become an owner, thereby making sure that the object stayed around for you to use until you were done with it, it was up to you to send a retain message to increase the retain count, and when you were done, to send a release message.

This was because the creator of the object (which caused the retain count to be set to 1) may have autoreleased it — sent an object a release message that will cause it to be released later (usually the next time the run loop is entered).

This is useful in situations in which you want to relinquish ownership of an object but avoid the possibility of its being deallocated immediately (such as when you return an object from a method). In either instance, you were maintaining a pointer to the object so that it could be used.

Although this approach was simple in theory, it was a real headache for programmers. The vast majority of system crashes occurred because apps ran out of memory and were shut down by the system. In some of these cases, the app didn’t respond to the memory warning methods and manage the low-memory warnings.

Most of the time, however, even if the app responded to the low-memory warnings, it was limited to what it could do because the memory was leaked. Memory was actually available because some objects were not being used, but those objects’ memory had not been released back to the system. There were no longer pointers to these objects, so they couldn’t be released and then deallocated and the memory reused.

Developers have had a number of ways to manage memory automatically. One is garbage collection, which scans through memory and releases objects that have no pointers to them.

Garbage collection for Objective-C was available on the Mac, but garbage collection has a few problems. It can start up and pause your apps at the most inopportune time, and it affects performance and the user experience because you have no control, or any idea, when it will occur. It was never implemented on iOS and is deprecated beginning with OS X 10.8 (Mountain Lion).

Having to do all this memory management in your app has changed with the latest versions of the Objective-C compiler, which now comes with automatic reference counting(ARC), which is enabled by default whenever you create a project. ARC does for you in the compiler what you used to have to do on your own. It handles all those releases, autoreleases, and retains for you.