Sei sulla pagina 1di 53

iPhone OS Memory Management

Robert Clair VTM Seattle April 25, 2010

Demo Memory basics Objective-C memory management review Low memory warnings and responding to them Keeping your memory usage low Tools for memory management

MemoryHog Demo

MemoryHog Demo The rest of the talk could be titled How not to go Boom. The basic problem: the devices dont have much memory and if you are piggy with memory, you are looking for toruble

Memory basics
Were interested in RAM, not the ash disk iPhone OS devices dont have much: Earlier devices: 128 MB iPhone 3GS, 3rd generation iPod, iPad: 256 MB By comparison latest Mac Book Pro (base model) has

Virtual Memory and Paging (Swapping)

The are not the same thing Virtual memory Each process sees its own address space MMU translates between virtual addresses and
physical addresses

Paging - system swaps pages of memory to and from

secondary storage (the disk)


iPhone OS devices
Virtual memory - YES ! Paging - NO ! (except...) The OS has paging, but its disabled (probably so
you dont wear out the ash disk).

Jail-breakers can enable it. The no paging only applies to writable memory
pages. Read only memory (program text, les that are memory mapped [mmap] read-only) is paged.

Virtual address space of a Unix process


Data Segment - global variables Stack - automatic variables (variables dened in

functions or methods)

Heap - dynamically allocated memory


Dynamic Memory
You can ask the system for memory at runtime. Dynamic memory comes from the heap Request memory with malloc (or cousin):
char* someBytes = malloc( numBytesNeeded );

As the heap grows your process size increases Return memory to the heap with free:
free( someBytes );


The Golden Rule of Dynamic Allocation

When you allocate memory from the heap you must give it back when you are nished using it !

iPhone Addendum
As soon as you can.


An aside about malloc

iPhone OS malloc does lazy allocation It notes your request, but doesnt commit pages for
the requested memory until you try and write on it.

This can get you in trouble if you think the memory is

there, but the system doesnt actually have the memory when you go to use it.


Objective-C memory management review

OC Objects are chunks of memory with an isa
pointer (points to the class object, tells what the object is)

All OC objects are heap-based Trying to create one on the stack is an error Under the hood the memory comes from malloc (or
a cousin like calloc) and is returned by free


On Mac OS X you can choose between reference

counting and garbage collection (GC)

No GC on the iPhone (no time? performance issues?) iPhone uses reference counting only


Reference Counting
Also called retain counting or managed memory. Each object keeps a count of how many other objects
are using it. (Usually discussed in terms of ownership.)

When an objects reference count becomes zero, the

object is deallocated and its memory is returned to the heap.


If you create an object, you own it: Foo* aFoo = [[Foo alloc] init]; You must eventually balance the creation by relinquishing your ownership with a release message: [foo release]; release decrements the objects reference count. If the new reference count is zero, the object is deallocated.


If you dont, you have a leak:

-(void) useAFoo { Foo* aFoo = [[Foo alloc] init]; // Do something with aFoo return; }


The rules:
If you create an object (receive an object from a
method that begins with alloc or new or contains the word copy) you own the object. do not own the object. If you want to keep the object (store it in an instance variable) you must take ownership of the object by sending it a retain message, which increments the objects reference count: [aFoo retain];

If you receive an object from any other method, you


Rules, continued
You may also take ownership by copying the object (if
it is capable of being copied, the objects class must implement -copyWithZone:).

In all cases, if you own an object, either because you

created it or you retained it, you must eventually relinquish your ownership by sending the object a release message


If a release message causes an objects retain count
to drop to zero, the release method invokes the objects dealloc method. variables, it must release them in the dealloc method.

If the object has stored other objects in its instance

The dealloc method must invoke [super

dealloc] as the last statement. It is NSObjects dealloc method that actually returns the objects bytes to the heap.



A problem (convenience constructors and such):

+ (Foo*) foo { Foo* aFoo = [[Foo alloc] init]; return aFoo; }

The solution: autorelease


Sending an object an autorelease message
registers the object to receive a release message sometime in the future. It places the object in an autorelease pool. release message (one for each time it received an autorelease message) scheduled for the future. It counts as a release for bookkeeping purposes.

When the pool is drained the object receives a

An autorelease is a release, just one that is

One last thought

Use properties and synthesize your accessors whenever you can: @property (nonatomic, retain) Foo* aFoo; @synthesize aFoo;

It will automate much of your memory management.


Low Memory Warnings

When the number of unused pages in memory drops
below a threshold, the system will evict read-only pages. If that does not free up enough memory it sends all running apps a low memory warning. Each app is expected to respond by releasing any memory that it is not currently using.

What do you mean all running apps? You cant multi-task, but Apple can. Safari, Mail, etc, may be running in the background. If the low memory condition persists, the system will
eventually terminate your app.

How much memory can an app safely use?

Unknowable - depends on what else is going on Apple DTS engineer: "You will get memory warnings.
They are a fact of life, and are generally unavoidable. That does not mean that you are doing anything wrong.



Handling Low Memory Warnings

In your application delegate implement:
- (void)applicationDidReceiveMemoryWarning:(UIApplication *) application

For other objects (except UIViewControllers) , register for low memory notication:


Registering for Low Memory Notications

[ [NSNotificationCenter defaultCenter] addObserver: self selector: @selector(lowMemory:) name: UIApplicationDidReceiverMemoryWarningNotification object: nil ]; ... - (void) lowMemory:(NSNotification *)notification { // Release anything you can }


- (void)didReceiveMemoryWarning

Default implementation checks to see if view can be

released (has no superview) and releases it if possible.

If it releases the view, it calls

- (void)viewDidUnload

3.0+, no need to override (use viewDidUnload), but if you

override it, remember to call super


- [UIViewController viewDidUnload]

Release anything that can be easily recovered. Release nib objects held in outlets Objects are stored in outlets using accessors if they exist Accessor must retain any top level nib object If there is no accessor the object is sent a retain message
when the nib is loaded

Invoke [

super viewDidUnload]

(?? - not clear in docs)


Dont block the main thread (run loop)

Low memory warnings are delivered on the main thread If you are doing something time consuming on the main
thread, you may never receive the low memory warning and your app may be terminated.

If your app uses a lot of memory on a secondary thread

you must nd a way to pass on the warning.


Keeping Memory Footprint Small

Dont leak (follow the rules). Dont pseudo-leak. Load resources lazily if you can. Be careful with images.


Size of compressed image le isnt relevant. In
memory, the image is roughly width * height * 4

Drawing a big image in a small space doesnt make it

smaller. Size your images to their nal size.

Different UIImage methods (imageNamed,

imageWithContentsOfFile:) do different things


+[UIImage imageNamed:]
Reads the le, uncompresses it, caches result Cached copy of data is kept even if the UIImage is

Low memory condition causes cache to be purged. No direct control over when cache is purged. Use for small frequently drawn images.


+[UIImage imageWithContentsOfFile:]
Just reads enough of le to determine if it can open
the le.

Reads and uncompresses the le each time it draws.

Uses uncompressed size worth of memory only temporarily.

Assigning a UIImage created this way toa

UIImageView or as the contents of a CALayer also causes it to read and uncompress the le. The UIImageView or CALayer keep the expanded version.

Demo image app


Memory Usage (in MB) 8.6 MB image used with UIImageView

imageNamed Initial Load UIImage Assign to UIImageView Remove from UIImageView 2.46 11.12 11.09 11.12 imageWithContentsofFile 2.46 2.51 11.09 2.51


Memory Usage (in MB) 8.6 MB image used with UIView subclass (stored in instance variable and drawn with drawinRect: )
imageNamed Initial Load UIImage Assign to UIView Remove from UIView 2.73 11.38 11.81 11.84 imageWithContentsofFile 2.73 2.78 3.20 3.04

Other ways to uncompress and cache a UIImage

Assign it to a UIImageView that isnt in the view


Draw it with CoreGraphics to get a CGImageRef,

then Use the CGImageRef to create a new UIImage


Other strategies:
Create your own caches for other large resources
(sounds, etc.)

Consider memory mapping (mmap) Use thumb instructions (Xcode default) if you are not
doing oating point work

Avoid autorelease if possible. (Opinions differ,

probably only in case of large objects.)



Bottom Line

Understand what you are doing Pay attention Use common sense


Tools for debugging memory problems

NSZombie Clang Static Analyzer Instruments Leaks template VM Tracker (In
Object Allocation template)

Object Allocation


First dealloc changes objects isa pointer so the object
becomes an instance of NSZombie

Zombies log messages You can set break point on messages to zombies:
[_NSZombie release]

Turn on zombies by setting NSZombieEnabled

environment variable to YES


Can set NSZombieEnabled in Xcode (project or target info) or in ~/.dbxinit:

define enable-zombies set env NSDebugEnabled=YES set env NSZombieEnabled=YES set env NSDeallocateZombies=NO set env NSHangOnUncaughtException=YES set env NSAutoreleaseFreedObjectCheckEnabled=YES fb -[_NSZombie retain] fb -[_NSZombie release] fb -[_NSZombie methodSignatureForSelector:] fb -[_NSZombie respondsToSelector:] fb -[_NSZombie class] fb -[_NSZombie forward::] fb -[NSException raise] end enable-zombies

The environment variables are dened in NSDebug.h


Clang Static Analyzer

checks for memory problems not perfect - some false negatives, more false






VM Tracker
part of the Object Allocation template Requires iPhone OS 3.1 and Mac OS 10.6