In creating my first iPhone app which uses the Address Book, I discovered the world of memory management with iOS. I wanted to share a few things I’ve learned and provide lots of links for your reference.
ARC, retain counts and CFRelease
Admittedly, if you stick strictly to Foundation code, you probably don’t need to be concerned too much about what happens under the hood. With the introduction of ARC (Automatic Reference Counting), iOS takes care of much of this for you. It’s when you also need to deal with Core Foundation (CF), which the Address Book uses, that things become tricky. CF has a concept of object ownership and memory management using retain counts. An object can have multiple owners (retain count > 1), but when you are done with that object the retain count had better be zero or you’ll create memory leaks. To accomplish this, you need to use CFRelease().
Over releasing, Zombie Objects, CFGetRetainCount
A typical problem I ran across was calling CFRelease too many times (over releasing). When you try to access an object that has been over released, you’ll get a run time message with “Program received signal: EXC_BAD_ACCESS”. To get a more meaningful message that will will show the type of object with the bad access, you can enable Zombie objects with ⌥ ⌘ R. This is discussed in the post How do I set up NSZombieEnabled in Xcode 4?
If you want to check the retain count on an object, you can log the result of calling CFGetRetainCount(<object>). Note that you if you call this function on an object that has been fully released, your program will crash.
Mixing Foundation and Core Foundation Code
iOS makes it fairly easy to mix Foundation and CF code. They introduced a concept called Toll-Free Bridging to make this easier. Many types (arrays,dictionaries, etc.) can be used interchangeably. What you need to think about is who owns the object after the conversion. If you want to convert with no change in ownership (I don’t use this often), then use __bridge. Example:
NSString *str = @"hello"; CFStringRef str_cf = (__bridge CFStringRef)str; // Foundation to CF NSString *str2 = (__bridge NSString *)str_cf; // And back again
When converting from Foundation to CF, use CFBridgingRetain(). Ownership transfers to CF which means that you are responsible for releasing it when done.
NSString *str = @"hello"; CFStringRef str_cf = (CFStringRef)CFBridgingRetain(str); // Do something with str_cf CFRelease(str_cf);
When converting from CF to Foundation, use CFBridgingRelease. Ownership transfers to Foundation.
CFStringRef str_cf = CFSTR("hello"); NSString *str = (NSString *)CFBridgingRelease(str_cf);
Returning a Core Foundation object from Foundation method
This one tripped me up for a while. Consider the following:
+ (void)populate { CFStringRef name_cf = [self getString]; // Do something with name_cf CFRelease(name_cf); // Over release! } + (CFStringRef)getString { CFStringRef name_cf = CFStringCreateWithFormat(... return name_cf; }
The problem here is that we didn’t transfer ownership of name_cf inside getString to the caller! So how do we cause the code in getString to release its retain count on name_cf when returning the string to populate?
After digging for a while, I discovered that I only needed to rename getString() to newString(). iOS then assumes that we want a transfer of ownership to the calling function. See the documentation Advanced Memory Management Programming Guide. Look for the section “You own any object you create”. I first ran across the answer at Stack Overflow with these posts:
How to release an locally created object, while it using as return object
Best IOS Memory management @ Function Call Returning allocated object
Coming up – Instruments, Heapshots, etc.
In my next post, I’ll share some of what I’ve learned with using Instruments and Heapshots for memory leak debug.
Pingback: The development of Contacts2Web | Finalize.com: My journey with iOS and other code adventures