Chapter 5: Memory Management with Objective-C ARC

The shift from the GCC compiler to the LLVM compiler gave Apple control over certain language and compiler-specific features. One arguably important feature is Automatic Reference Counting (ARC). For the most part, ARC “just works.” On a new project, things are easy, but when you try to migrate an existing code base to use Objective-C ARC, you will probably find that the road is long and winding.

In Chapter 2, I showed you how to use Xcode’s built-in Objective-C ARC migration tool. In this chapter, I show you how to migrate your code base to ARC and solve the most commonly faced errors by finding out how ARC works. Let’s get started.

Introduction to Objective-C ARC

In a nutshell, ARC is a memory management model where the compiler automatically inserts retains and releases appropriately for you. This means that you don’t have to worry about managing memory and focus or writing the next great app. You’re relieved from worrying about leaks, for the most part.

A Brief History

Prior to ARC, Apple platform developers used manual reference counting memory management on iOS. On Mac, developers had a choice. They could choose either garbage collection memory management or manual reference counting memory management. Garbage collection is mostly a high-level language (C# or Java) feature that non-deterministically manages memory for you. A programming language is designed either for writing high-performance applications or for writing software that behaves like a daemon process, running mostly in the background, servicing requests. Java, C#, and other languages fall into the latter category, whereas derivatives of C, namely C++ and Objective-C, are languages that were designed to write native code where high-performance code was crucial. High performance can be achieved only when memory management is deterministic. Garbage collection, although it sounds good theoretically, fares poorly when implemented for a language like C mainly because it’s non-deterministic. Non-deterministic memory management means that as a developer, you will not know when a memory you allocate will be released. Apple devices like the iPhone and iPad have very limited memory on board, which means that apps should be designed to run within their resource constraints. Deterministic memory management was, in fact, one of the design goals of iOS, which is why iOS supported only a reference counting memory management model.

Manual Versus Automatic Reference Counting

Until iOS 5, the memory management model in iOS was manual reference counting where the developer kept track of the number of references to every allocated memory block. Good coding practices such as using properties (instead of using ivars directly) to read and write a variable (and such) eases manual memory management to some extent. But even for a highly skilled developer, manual memory management is still overkill. Many developers continue to depend on autorelease pools. For example, compare the following two code blocks:

Manual Memory Management Code without autorelease

NSDictionary *myDict = [[NSDictionary alloc] init];

// Use the dictionary here

. . .

// more code

. . .

// I don’t need my dictionary any more, let’s deallocate the memory

[myDict release];

Manual Memory Management Code with autorelease

//autoreleased object

NSDictionary *myDict = [NSDictionary dictionary];

// use the dictionary here

. . .

// more code

. . .

// I don’t need my dictionary any more, but let’s leave it to the

// autorelease pool to release the dictionary for us

In the first block of code, you create a dictionary and release it when you no longer need it. This kind of code is highly deterministic and has better performance. You know exactly when the memory associated with the dictionary is released. In the second block of code, you’re at the mercy of the autorelease pool to drain off the memory allocated during that run loop. Although this code might not show up as a huge performance problem in most cases, code like this slowly accumulates and makes the app heavily dependent on autorelease pools. Even objects that you know can be deallocated earlier linger around until the autorelease pool drains them off, making the memory management model slightly non-deterministic.

What Is ARC?

ARC is a compiler-level feature that automatically inserts retains and releases for you, which means that when you use ARC, you don’t have to raise the minimum deployment target. Though ARC was announced with iOS 5, your app compiled with ARC can run on iOS 4 devices as well. This also means that every app you write today should use the ARC compiler, and there is no reason to do otherwise.

In the first code block, just shown, after allocating memory, you don’t have to write the [dict release] statement. ARC inserts it automatically for you. In fact, when you compile the file with the ARC flag turned on, you will get a compile time error when you call release or retain. Within a project, you can mix and match ARC code with non-ARC code. You do so by using the two compiler flags, –fno-objc-arc and –fobjc-arc. In the next section, you find out when to use which flag. You can even mix and match ARC code and non-ARC code within the same class!

Using Non-ARC Code within a File Compiled with ARC

#if !__has_feature(objc_arc)

// Non ARC code goes here

-(void) release {

  // release your variables here

}

#endif

The Clang language extension __has_feature function-like macro is even more powerful. You can use it with any supported Clang language feature to conditionally compile code.

Integrating Non-ARC Third-Party Code into Your ARC Project

When you use a third-party framework that isn’t compiled with ARC, you compile files from that library using the –fno-objc-arc compiler switch. (Refer to Chapter 2 to find out how to add a compiler switch to a file.) In Chapter 2, you learned how to use Xcode’s Convert to Objective-C ARC tool. When you use that tool to migrate, you can choose a list of files that must be excluded from migration (refer to Figure 2-10 in Chapter 2). All these excluded files will have the –fno-objc-arc compiler switch automatically added by the migration tool. If you don’t add this switch, the ARC compiler is going to throw errors for all retain/release statements in your third-party library.

Integrating ARC Code into Your Non-ARC Project

Sometimes you will find a third-party library that is compiled using ARC. Unlike the contrary, when you use this code in your non-ARC project, you will not get a compiler time error. You will instead be leaking memory. To avoid this situation, compile the ARC ready third-party source code with –fobjc-arc.

ARC Code in a Framework

If you’re writing a framework, either for publishing it as open source or for sharing it with other teams within your company, you need to warn developers who use your code that the files should be compiled with ARC. Accidentally using ARC code in a non-ARC project leaks memory, and it’s your responsibility to warn developers. You do so using a macro as shown here:

#if ! __has_feature(objc_arc)

#error This file is ARC only.

#endif

The preceding code block should be familiar. It’s the same Clang language extension explained in the previous section.

Zeroing Weak References

Earlier in this chapter, I said that ARC is a compile time feature, But there’s more. Starting from iOS 5 and Mac OS X 10.7 (Lion), the runtime supports something called zeroing weak references. Weak references are used to avoid retain cycles by allowing you to hold onto a pointer without bumping the retain count. But when the memory (that is pointed to) gets deallocated, the weak reference will be pointing to garbage. Accessing that pointer after the memory is deallocated crashes your app. Zeroing weak references solves this problem by ”nil-ing” out pointers when the memory gets deallocated.

Ownership Qualifiers

ARC works by inserting retains and releases automatically for you. But the “automatic” code generator needs some hints to know when exactly it should release the allocated memory. ARC uses the ownership qualifiers you specify to determine the lifecycle of a pointer. Ownership qualifiers are arguably the most esoteric concept in ARC. After you understand ownership qualifiers, you will start thinking in terms of object graphs rather than retains and releases. ARC supports four kinds of ownership qualifiers:

__strong

__weak

__unsafe_unretained

__autoreleasing

The first three can also be used in property declarations. When an ownership qualifier is used with a property, you don’t prefix it with the double underscore (__). The default ownership qualifier (when you don’t specify one) is __strong.

In Xcode 4.2/LLVM 3, the default ownership qualifier for stack variables was __strong and for properties it was __assign. From Xcode 4.3/LLVM 3.1 and higher the default ownership qualifier for properties was also changed to __strong. The migration wizard in Xcode 4.3 was updated to reflect these changes.

__strong

You specify strong ownership using either the qualifier

__strong NSString* myObject = nil;

or

@property(nonatomic, strong) NSObject *myObject;

When declaring properties, you use the second syntax. A strong ownership is synonymously equivalent to a “retain.” It bumps up the retain count. Thinking in ARC, you use strong ownership for holding references to anything you “own.” Strong ownership includes a view owning its subviews, a parent controller owning its child view controllers, and contained objects. As I already mentioned, __strong is the default ownership qualifier. You are not obliged to write the keywords __strong or strong to specify strong ownership. The preceding two lines of code can henceforth be written as follows.

NSString* myObject = nil;

or

@property(nonatomic) NSObject *myObject;

__unsafe_unretained

When you don’t own a pointer but want to hold a reference to it, you use the _unsafe_unretained ownership qualifier. The following lines show use of this qualifier:

__unsafe_unretained UIView* mySubview;

or

@property(nonatomic, unsafe_unretained) UIView* mySubview;

A common use of this qualifier is to maintain references to a subview in a view controller. Remember that when you create a view and add it to another view using the addSubview: method, the super view “owns” the subview. Sometimes, in your view controller, you may want to own another reference to this subview.

Figure 5-1 illustrates a frequently used ownership qualifier pattern. A UITextField is normally added as a subview of a UIView, and the UIViewController maintains a reference to it without owning it.

9781118449974-fg0501.tif

Figure 5-1 Ownership of a UITextField

You can use __unsafe_unretained in many similar cases including maintaining a reference to a delegate. When used with properties, __unsafe_unretained is synonymous to the assign modifier that you used prior to ARC.

__weak

The __weak modifier behaves exactly like the __unsafe_unretained modifier, except that when the pointer that is being pointed to gets deallocated, the weak pointer becomes nil. Of course, this requires runtime support. As such, weak references are supported from iOS 5 and higher.

Unlike the rest of ARC, weak references are not a compile-time setting. The deployment runtime needs to support zeroing weak references. If your minimum deployment target is lower than iOS 5.0, you cannot use weak references. The Xcode Convert to Objective-C ARC migration wizard will convert your “assign” modifiers to __unsafe_unretained instead of __weak if your deployment target doesn’t support zeroing weak references.

The use of a weak reference is similar to __unsafe_unretained. You use a weak reference in places where you normally use __unsafe_unretained on runtimes that support zeroing weak references.

__auto_releasing

__auto_releasing is an ownership qualifier that you use when you want to return an auto-released variable from a method. NSError objects that are allocated in a method and returned to the caller via a pointer to a pointer use __auto_releasing ownership. The one important difference between this qualifier and the other three is that __auto_releasing cannot be used with properties.

Ownership qualifiers, unlike const modifiers, are position-independent. So __weak NSObject* myObject; and NSObject __weak *myObject; mean the same. This is because ARC ownership qualifiers apply to the pointer and not the value pointed to (unlike a const modifier). The LLVM compiler can henceforth deduce your intent.

ARC nils Declared Variables

Another important benefit of using ARC is that all uninitialized variables are “nil-ed” by default. This means that a declaration like

NSObject *myObject1, *myObject2;

points to nil when compiled with ARC. However, note that, unlike other high-level programming languages, ARC doesn’t automatically set a scalar variable to zero.

This means that the variables declared in the following lines will not equate to zero:

int a;

int b;

Objective-C Naming Conventions

ARC honors the Objective-C method family and performs memory management automatically. In Objective-C, all methods that start with alloc, copy, mutableCopy, and new create a new object and bump up the retain count. In other words, you need to deallocate the memory associated with this object when you no longer need it. When you use ARC, it will release any Objective-C pointer returned by a method that is expected to return a retainable object pointer type in the calling method. However, there is a caveat.

If you write a method called newPersonName, in the Person object, ARC assumes that the method returns a newly allocated object. Everything goes well when the calling code and the method, newPersonName, are compiled using the ARC compiler (or using a non-ARC compiler). But if this method is in a class that is not compiled with ARC and the calling code is compiled with ARC, you will crash. Conversely, if the newPersonName method is compiled with ARC but your calling code isn’t, the allocated memory will leak.

Overriding the Default Behavior

Although technically you can’t override LLVM’s behavior, you can change a method’s family using the Clang source annotation, NS_RETURNS_RETAINED and NS_RETURNS_NOT_RETAINED. Your newPersonName method can be annotated as shown here to tell the ARC compiler not to assume that this method, despite starting with new, returns an unretained object pointer.

-(NSString*) newPersonName NS_RETURNS_NON_RETAINED;

Another way to tell the ARC compiler that this method returns a unretained object pointer is to rename the method to something like –(NSString*) personName;. But if your implementation is in a static library and you don’t have access to the source code, you will not be able to rename methods. The two Clang source annotations we showed can be used in these cases.

Toll-Free Bridging

Unlike the Objective-C libraries, older C-based or Core Foundation class libraries (CF* methods) that you use in Objective-C don’t follow an established set of naming conventions. This means that the ARC compiler cannot, with 100 percent certainty, release memory when it’s done. Prior to ARC, you can bridge between a CF* object to a NS* object with a cast (also known as toll-free bridging). That is, you can cast a CFString* to an NSString* just by typecasting. With ARC, you can’t do this anymore, at least without specifying an ownership transfer modifier.

ARC allows the following ownership transfer modifiers:

__bridge

__bridge_retained

__bridge_transfer

__bridge

The first modifier, __bridge, is a plain cast that tells ARC not to bump retain count or change ownership. The designers of the LLVM compiler didn’t make __bridge the default ownership transfer modifier, because it would be preposterous to make such a bold decision. Because Core Foundation methods don’t have a proper naming convention, the compiler cannot make an educated decision about how to transfer the ownership of a C pointer type without you specifying one explicitly.

__bridge_retained

You use the second modifier when you want to transfer the C pointer type by bumping up the retain count. You use this modifier when you want to return a retained pointer from an Objective-C method that creates a Core Foundation object and releases the object using a CFRelease method. You normally return a retained pointer if your Objective-C method belongs to the NSRETURNS_RETAINED family.

__bridge_transfer

You use the last modifier when you want to transfer a Core Foundation pointer type to an Objective-C pointer by bumping the retain count. You use this modifier when your Core Foundation method creates an object and you want the object’s memory management to be handled by ARC. You’re essentially “transferring” ownership to ARC.

Two macros, CFBridgingRetain and CGBridgingRelease, can be used in place of this typecast. You find out more about these Core Foundation macros and ARC in Chapter 27 and Chapter 28. Chapter 27 also covers toll-free bridging in detail.

ARC Internals

ARC, as you know already, inserts retain and releases automatically for you. The ARC compiler has two parts, the front-end compiler and the optimizer.

Front-End Compiler

The front-end compiler works by inserting a matching release for every “owned” object. An object is owned if its ownership qualifier is __strong. Objects created within a method scope are torn down by a release inserted automatically at the end of the method. Objects owned by the class (ivar/properties) are torn down in the dealloc method. In fact, you don’t have to write a dealloc method or call the super class’s dealloc. ARC does all these automatically for you. Moreover, this compiler-generated code even performs better than a release written by you because the compiler can make some assumptions. Under ARC, since no class can override the release method, there is no reason to invoke it. Instead, ARC optimizes the call by directly calling the objc_release. The same applies to retain as well. ARC optimizes the call by emitting an objc_retain rather than a retain message.

ARC Optimizer

Although the front-end compiler sounds superior, the emitted code may at times contain duplicate calls to retain and release. The ARC optimizer takes care of removing unwanted retains and releases which ensures that the generated code runs as fast (or faster) than an equivalent manual reference-counted code.

You will find out more about the internal workings of ARC in Chapter 28.

Common ARC Migration Errors

Earlier in this chapter, you read about ARC and how to handle toll-free bridging under ARC and how ARC works internally. Following are some of the common migration errors that you will encounter while migrating to ARC.

Casting an Objective-C pointer type to a C pointer type (and vice versa)—Because ARC doesn’t automatically do toll-free bridging, when you convert an Objective-C pointer to a C pointer, you have to use an ownership transfer qualifier. You will encounter this error when trying to compile a source code that relies heavily on Core Foundation library.

Receiver type doesn’t declare the method with selector—ARC relies on the method family to determine how to release/retain the memory returned by a method. If you call a method on an object, ARC mandates that the method be defined. Prior to ARC, you can still call the method even if it is not defined or declared. Calling such a method results in a runtime exception. Object doesn’t respond to this selector. With ARC, this is no longer the case. The ARC compiler flags a compile time error when it encounters this situation.

LLVM 3.0 mandates that you define all private methods inside a category. Calling a private method that isn’t defined but declared after the calling code results in a compile time error. (Calling a private method that isn’t defined but declared before the calling code works). LLVM 3.1 is a two-pass compiler that discovers methods declared within the same file, which means that you can omit private method definitions in the implementation file. Nevertheless, for the sake of writing clean code, I recommend defining all private methods or at least the ones that you will call (excluding IBActions) in the implementation file within a category. In fact, Xcode 4.3 and higher automatically create an empty private category for you (For example: The default templates like the UIViewController subclass have an empty private category).

performSelector may cause a leak because its selector is unknown—This error again roots down to the same fact that the ARC compiler needs to know the method family of every method call. This is not a problem if you invoke a selector using its name. When you call a selector defined in a SEL variable, ARC cannot deduce if the selector method belongs to the family of methods that “creates” objects (NS_RETURNS_RETAINED) or the family of methods that returns an auto-released object (NS_RETURNS_NOT_RETAINED). The compiler emits this to warn you to do the memory management manually.

Clang doesn’t have a source annotation that helps to imply the method family of a selector. Purists may opt to suppress this warning forcefully using a pragma.

#pragma clang diagnostic push

#pragma clang diagnostic ignored “-Warc-performSelector-leaks”

    [self performSelector:self.mySelector];

#pragma clang diagnostic pop

Note that this pragma only suppresses the warning and doesn’t fix the leak if your method returns a created/retained object.

Implicit conversion of an Objective-C pointer to ‘void *’ is disallowed with ARC—With ARC, you can no longer cast a void pointer to an id and vice versa. ARC depends on ownership qualifiers to determine when to release the allocated memory. When you do a simple cast between id and a void pointer, you need to use a qualifier so that the ARC compiler knows how to treat the pointers. You do this using a bridge cast that you learn in one of the earlier sections, “Toll-Free Bridging.” Here is an example:

id selfPointerAsId = (__bridge void*)self;

ARC forbids Objective-C objects in structs or unions—ARC mandates that you use an Objective-C object rather than a struct when your struct has an Objective-C object. Structs don’t have a release method (or a destructor in C++ terms). As such, when you want to release a struct, you should also ensure that you release the memory allocated for objects within a struct. ARC works only when ownership qualifiers are clear. You own the object, and the object owns all of its sub-objects. An Objective-C object has init and release/dealloc methods that take care of setting it up and tearing it down. This means that when you “own” an object (think strong ownership), you don’t have to worry about ownership of the sub-objects. With a struct, it becomes impossible to implement such ownerships. The backbone of ARC, the ownership qualifier, breaks when a struct contains an object and this is the reason why ARC forbids Objective-C objects within a struct.

However, note that a struct for representing a mathematical complex number like the one shown here is perfectly valid.

typedef struct _ComplexNumber {

  

double realValue;

double imaginaryValue;

} ComplexNumber;

Under ARC, you can’t use an object/pointer within a struct. Everything else is valid.

‘NSAutoreleasePool’ is unavailable: not available in automatic reference counting mode—ARC deprecates NSAutoreleasePool in favor of a more powerful @autoreleasepool block. In fact, the Convert To Objective-C ARC tool converts your NSAutoreleasePool block in the main.m to an @autoreleasepool block like this:

  @autoreleasepool {

      return UIApplicationMain(argc, argv, nil,

      NSStringFromClass([AppDelegate class]));

  }

The @autoreleasepool block is much faster (up to six times faster) than the previous NSAutoreleasePool block. In fact, even if you don’t use ARC, you should at least use @autoreleasepool. The @autoreleasepool block is an LLVM feature and can in fact be used independently of ARC.

Workarounds When Using ARC

ARC has its own share of disadvantages, with the biggest being retain cycles. To add to this disadvantage, the LLVM static analyzer doesn’t detect a retain cycle.

Retain Cycles

A retain cycle occurs when two objects have mutual ownership of each other. A classical real-life scenario is when a child view controller owns a strong pointer to the parent view controller, as shown in Figure 5-2.

9781118449974-fg0502.tif

Figure 5-2 Retain cycles happen when you own a reference to your own parent

To avoid such retain cycles, use the __weak (or __unsafe_unretained) ownership qualifier. A weak reference ensures that the pointer’s retain count isn’t bumped up, as shown in Figure 5-3.

9781118449974-fg0503.tif

Figure 5-3 Avoiding retain cycles using a weak reference to your own parent

If you have learnt to think in terms of ownerships rather than release/retains, you can avoid retain cycles in most cases, except, well, blocks. Blocks and ARC hate each other, to some extent.

Blocks and ARC

A block is an ad hoc piece of code that captures the context. That is, any variable that can be accessed outside the block can be accessed within the block. Capturing the context yields surprisingly new design patterns and Chapter 22 explains them in detail. When a block “captures” the context, it makes a copy of every scalar variable in its scope. Every Objective-C object within the scope is retained. A common mistake that happens is when a block is owned by “self,” and within the block, you update ivars.

Code that Causes a Retain Cycle

self.myBlock = ^(NSString* returnedString) {

self.labelControl.text = returnedString;

};

The preceding code shows a classical pitfall when using blocks with ARC. The block, which is retained by self, captures self (retain) again. This causes a retain cycle. Very subtle, yet very dangerous.

To avoid this situation, capture an unretained reference to self within a block. Prior to ARC, you used the __block keyword along with __unsafe_unretained to make an unretained copy of a reference and used that reference within a block.

Using __block to Avoid Retain Cycles (Non-ARC)

__block id safeSelf = self;

self.myBlock = ^(NSString* returnedString) {

safeSelf.labelControl.text = returnedString;

};

ARC changes the semantics of __block, and you shouldn’t use it. Under ARC, __block references are retained rather than copied, which means that the preceding code still causes a retain cycle under ARC. The correct practice is to use a __weak (or unsafe_unretained) reference, as shown in the following code:

Using __weak to Avoid Retain Cycles (ARC)

__weak id safeSelf = self; //iOS 5+

//__unsafe_unretained id safeSelf = self; //iOS 4+

self.myBlock = ^(NSString* returnedString) {

safeSelf.labelControl.text = returnedString;

}

Summary

In this chapter, you found out about the ins and outs of ARC. I later explained the migration process and the various scenarios to avoid when using ARC.

Lastly, you read about the drawbacks and retain cycles associated with ARC. Fret not; within a few months of using ARC, you will become proficient in writing code that avoids retain cycles. Despite those minor drawbacks, I still recommend that you use ARC in all future projects.

Further Reading

Apple Documentation

The following document is available in the iOS Developer Library at developer.apple.com or through the Xcode Documentation and API Reference.

Memory Management Programming Guide

WWDC Sessions

The following session videos are available at developer.apple.com.

WWDC 2011, “Session 322: Objective-C Advancements in Depth”

WWDC 2011, “Session 323: Introduction to Automatic Reference Counting”

WWDC 2011, “Session 308: Blocks and Grand Central Dispatch in Practice”

WWDC 2012, “Session 406: Adopting Automatic Reference Counting”

Blogs

MK blog. (Mugunth Kumar). “Migrating your code to Objective-C ARC”http://blog.mugunthkumar.com/articles/migrating-your-code-to-objective-c-arc

mikeash.com. “Friday Q&A 2010-07-16: Zeroing Weak References in Objective-C”http://mikeash.com/pyblog/friday-qa-2010-07-16-zeroing-weak-references-in-objective-c.html

LLVM. “Automatic Reference Counting”http://clang.llvm.org/docs/AutomaticReferenceCounting.html

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.57.219