(FWD) ARC introduction iOS

http://www.drdobbs.com/mobile/automatic-reference-counting-on-ios/240000820?pgno=1

 

Managing Objective-C objects within an iOS application is always challenging due to the fixed amount of physical memory and the limited battery life of handheld devices. To address these challenges, Apple has introduced a new approach called “automatic reference counting” (ARC). In this article, I compare ARC against explicit retain/release and against garbage collection, as well as demonstrate automatic reference counting in an iOS project, and review some key guidelines on using ARC.

Readers should have a working knowledge of Objective-C and of the Xcode IDE.

Managing by Messaging

Initially, I manage ObjC objects through explicit messaging. I create the object using allocand init messages (Figure 1). I send a retain message to hold onto the object, and arelease message to dispose of it. ObjC objects made with alloc/init start with an internal retain count of one. A retain message increases that count by one, while a releasemessage decreases it by one. When the retain count reaches zero, the object self-destructs, freeing memory it has reserved.

 
Figure 1.

 

We can also use a factory method to create the ObjC object. This marks the object forautorelease, adding its pointer to the autorelease pool (Figure 2). We can do the same to analloc/init object by sending the latter an autorelease message.

 
Figure 2.

 

The pool checks its collection of object pointers during each event cycle. When it finds an object that is out-of-scope and has a retain count of one, it disposes of that object with arelease message. To deter disposal, we can send one or more retain messages to the autoreleased object. Then to allow disposal, we counter the retains with an equal number ofrelease messages.

Explicit messaging is still the best way to manage ObjC objects within an iOS application. It takes almost no overhead, it is easy to debug, and it can be fine-tuned for performance.

On the other hand, explicit messaging is prone to errors. An uneven number of retain andrelease messages can cause a memory leak or an EXC_BAD_ACCESS error. An explicitrelease to an autoreleased object can also lead to an EXC_BAD_ACCESS. And a collection object (like an array or a set) may not self-destruct when some of its entries have retain counts greater than one.

Managing by Garbage Collection

MacOS X 10.5 (Leopard) gave us another way to manage ObjC objects — garbage collection. Here, each Cocoa application gets its own collection service, which runs as a secondary thread (Figure 3).

 
Figure 3.

 

The service identifies all the root objects created right after launch time, then tracks every object created afterwards. It checks each one for scope and for strong references to a root. If the object has these attributes, the service lets it persists (marked in blue). Otherwise, it disposes of the object with a finalize message (marked in red).

The collection service is a conservative one. It can be interrupted, even suspended, when high performance is required. It is a generational service. It assumes the most recent objects to have the shortest lifespans.

Access to the collection service is through the class NSGarbageCollector. With this class, I can disable the service or change its behavior. I can even assign new root objects or reset the service itself.

Garbage collection removes the need for explicit retain and release messages. It can reduce dangling pointers and guard against null pointers. On the other hand, it requires all custom ObjC objects to be updated. Clean-up code must go into the finalize method, not thedealloc method. The ObjC object also must send a finalize message to its parent.

Next, the collection service needs to know when an object reference is weak. Otherwise, it assumes all references to be strong. This can lead to circular references and to memory leaks. The service also ignores objects created with malloc(): Those should be disposed of manually or created using the Cocoa function NSAllocateCollectable().

Finally, the service still incurs a performance hit despite being conservative. This is one reason why garbage collection is absent on iOS.

 

Enter ARC

ARC is an innovative approach that has many of the benefits of garbage collection, but without the performance costs.

Internally, ARC is not a runtime service. It is, in fact, a deterministic two-part phase provided by the new Clang front-end. Figure 4 shows the two parts of that phase. In the front-end phase, Clang examines each pre-processed file for objects and properties. It then inserts the right retainrelease, and autorelease statements based on some fixed rules.

 
Figure 4.

 

For example, if the object is allocated and local to a method, it gets a release statement near the end of that method. If it is a class property, its release statement goes into the class’dealloc method. If the object is a return value or is part of a collection object, it gets anautorelease statement. And if the object is weakly referenced, it is left alone.

The front-end also inserts retain statements for objects not locally owned. It updates any accessors declared with the directive @property. It adds calls to the parent’s deallocroutine, and it reports any explicit management calls and any ambiguous ownership.

In the optimize phase, Clang subjects the modified sources to load balancing. It counts theretain and release calls made for each object, then pares them to the optimal minimum. This avoids excessive retains and releases, which can impact on overall performance.

To demonstrate, look at the sample code in Listing One.

Listing One

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@class Bar;
@interface Foo
{
@private
    NSString *myStr;
}
@property(readonly) NSString *myStr;
 
- (Bar *)foo2Bar:(NSString *)aStr;
- (Bar *)makeBar;
//...
@end
 
 
@implementation Foo;
@dynamic myStr;
 
– (Bar *)foo2Bar:(NSString *)aStr
{
    Bar *tBar;
     
    if (![self.myStr isEqualToString:aStr])
    {
        myStr = aStr;
    }   
    return ([self makeBar]);
}
 
- (Bar *)makeBar
{
    Bar *tBar
    //...
    //... conversion code goes here
    //...
    return (tBar);
}
//...
@end

Here, I show an ObjC class bereft of any retain/release messages. It has one private property myStr, which is an instance of NSString (line 5). It declares a read-only getter, also named myStr (line 7). It defines a modifier foo2Bar and an internal function makeBar (lines 18-36). The class also imports the header for class Bar using the @class directive (line 1).

Listing Two shows the same sample code post-ARC.

Listing Two

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
@class Bar;
@interface Foo
{
@private
    NSString *myStr;
}
@property (readonly) NSString *myStr;
 
- (Bar *)foo2Bar:(NSString *)aStr;
- (Bar *)makeBar;
//...
@end
 
 
@implementation Foo;
@dynamic myStr;
 
– (Bar *)foo2Bar:(NSString *)aStr
{
    Bar *tBar;
     
    if (![self.myStr isEqualToString:aStr])
    {
        [aStr retain];
        [myStr release];
        myStr = aStr;
    }   
    return ([self makeBar]);
}
 
- (Bar *)makeBar
{
    Bar *tBar
    //...
    //... conversion code goes here
    //...
    [tBar autorelease];
    return (tBar);
}
//...
 
- (void)dealloc
{
    [myStr release];
    [super dealloc];
}
@end

The class interface remains unchanged. But its foo2Bar modifier got two new statements. One statement sends a release message to property myStr (line 24). Another sends aretain statement to argument aStr (line 25). The makeBar function sends an autoreleasemessage to local tBar before returning the latter as its result. Finally, ARC overrides the class’ dealloc method. In that method, it releases the property myStr (line 44) and invokes the parent’s dealloc method (line 45). If a dealloc method is already present, ARC will update its code appropriately.

Since ARC decides on its own how an ObjC object should be managed, it cuts the time spent on developing the class code. It prevents any dangling and null pointers. It can even be disabled on a per-file basis. This last feature lets the programmer reuse legacy sources of proven reliability.

But the Clang compiler is built into LLVM 3.0, which is available only on Xcode 4.2 and newer. Optimized runtime support for ARC is present only on MacOS X 10.7 (Lion) and iOS 5.0. It is possible to use ARC in iOS 4.3 binaries through glue code. It is also possible to use ARC in OS X 10.6 binaries, provided the latter does not employ any weak pointers

Next, ARC works exclusively with ObjC sources. It has no effect on PyObjC and AppleScriptObjC sources. It does, however, affect the underlying ObjC code that bridges each PyObjC and ASOC class to Cocoa. Also of note, some third-party frameworks may cause errors when compiled with ARC enabled. Make sure to contact the framework developer for updated versions.

 

Preparing for ARC

There are two ways to support ARC in an iOS project. One way is to use an ARC-enabled template to create the project. Another is to have Xcode adapt an existing project for ARC.

Suppose you want to start a new iOS project. Launch Xcode and choose New Project from the File menu. On the new assistant dialog (Figure 5), select a project template (in this example, Single View Application), and click Next to view the project options. Enter the project name, company ID, and class prefix in the fields provided. Then click to set the check-box Use Automatic Reference Counting (Figure 6). Click Next to view the project location. Set the location and the source repository (optional), then click Create to create the project itself.

 
Figure 5.

 

 
Figure 6.

 

To verify if ARC is enabled, select the project icon from the Groups and Files pane in the project window (Figure 7). Click on Build Settings from the Target Settings toolbar, then click All underneath that toolbar. Scroll down and locate the settings group Apple LLVM computer 3.0 — Language. Look for the entry labeled Objective-C Automatic Reference Counting — it should have a value of Yes.

 
Figure 7.

 

What if you want to adapt an existing iOS project to ARC? Open the project into Xcode and click on the Edit menu. From the Refactor submenu (Figure 8), choose Convert to Objective-C ARC… This brings up another assistant dialog summarizing the steps to be taken.

 
Figure 8.

 

Click Next to see a list of build targets (Figure 9). Select a target and click Precheck to start refactoring. If refactoring fails, Xcode will alert the user, and it will list the refactor errors on the project window.

 
Figure 9.

 

If the refactor succeeds, Xcode goes into compare mode (Figure 10). The assistant dialog splits into three panes. The left pane displays the affected files, with the first file selected. The middle pane show the original source text, the right pane shows the modified source. Review the proposed changes and click Save to commit them or Cancel to dispose of them. Make sure to have a code repository in place, however. Otherwise, you will be unable to revert the project files to their pre-ARC state.

 
Figure 10.

 

What if you want to exclude some project files from ARC? In this case, click to clear the checkbox next to each file while Xcode is in compare mode (see Figure 10). If the file was added to a refactored project, select the project icon from the Groups and Files pane. Click Build Phases on the Target Settings pane, and scroll down to the group Compile Sources (Figure 11). Click on the group header and select which files to exclude. Then click into the Compile Flags column and enter -fno-objc-arc into the modal dialog. Click Done to set the flag, which should appear next to each file.

 
Figure 11.

 

Guidelines for ARC

When writing ObjC code from scratch, you should ensure the code is compatible with ARC. That code must give ARC the clues it needs to do its tasks. Otherwise, it may insert statements in the wrong spots, or worse, it may report false errors in the code.

Here are some guidelines for writing ARC-compatible code. These are compiled from the official Apple docs and from blog articles listed at the end of this article.

  • Let ARC decide how and when to retain and release objects.
    As stated earlier, the ARC front-end catches any explicit retain and release messages. It will report them as errors, and you will have to remove them manually to let the project compile. So never send a retain or release message to an ObjC object. Never check the object’s retain status with a retainCount message because that message is no longer reliable. Also, do not use the directive @selector to invoke the object’s retain andrelease methods.
    As for @property accessors, declare them without the attributes assigncopy, or retain. Let ARC decide on which attribute to give each accessor.
  • Let ARC manage the autorelease pool and its objects.
    Again, the ARC front-end catches any explicit autorelease messages. Make sure to exclude these messages in your ObjC code.
    Also, do not use NSAutoreleasePool to create the pool. Instead, use the @autoreleasedirective to mark the location and scope of the pool. This tells the front-end to insert statements that create and dispose of pools optimized for ARC.
    Consider the sample code in Listing Three. 

    Listing Three

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
      void main()
    {
        NSArray *tArgs = [[NSProcessInfo processInfo]
            arguments];
        unsigned tCnt, tLmt = [tArgs count];
         
        @autorelease
        {
            for (tCnt = 0; tCnt < tLmt; tCnt++)
            {
                @autorelease
                {
                NSString *tDat;
                NSString *tNom;
                 
                tNom = [tArgs objectAtIndex:tCnt];
                tDat = [[[NSString alloc]
                    initWithContentsOfFile:tNom]
                    autorelease];
                 
                // Process the file, creating and
                // autoreleasing more objects
                }
            }
         
        // Do more tasks, creating and autoreleasing
        // more objects
        }
         
        // Do whatever cleanup is needed
        exit (EXIT_SUCCESS);
    }

    Here, the main() entry function uses two autorelease pools. The first pool appears near the start of the function (line 7) and stays active until the function ends (line 28). Any objects marked for autorelease by the function go to this pool.
    The second pool appears at the start of a for-loop cycle (line 11-23). As each cycle ends, it disposes of the pool and creates a new one in its place. Objects marked for autorelease during each loop go to this pool, not to the first one.

  • Declare explicitly any weak and unretained references.
    ARC assumes all object references to be strong. There are exceptions, of course, the best example being the window view (Figure 12). The view keeps strong references to each subview and widget it contains. On the other hand, each subview and widget only keeps a weak reference to its parent view.

     
    Figure 12.

    However, it is possible for two ObjC objects to refer to each other strongly. When this happens, you get a circular reference. Consider the sample class in Listing Four. 

    Listing Four

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
      @interface FooLink
    {
        FooLink *fooNext;
        FooLink *fooPrev;
        NSData *fooData;
        NSInteger fooID;
    }
    @property(readwrite, assign) FooLink *nextLink;
    @property(readonly) FooLink *prevLink;
     
    - (NSData *)getData;
    - (void)addToLink:(NSData *)aDat;
    - (FooLink *)searchForLink:(NSInteger)anID;
    - (BOOL)hasLink:(NSInteger )anID;
    //...
    @end

    Class FooLink creates a double-linked list, with each link holding an NSData instance. Properties fooNext and fooPrev hold pointers to other instances of FooLink. Since both pointers form a strong reference, ARC will not dispose of the instance.
    To prevent this, declare one of the properties as weak. In Listing Five, I placed the directive __weak before the property fooPrev (line 4). Thus, when fooNext points to a nil and the FooLink instance is out of scope, ARC can safely send a release message to the instance.

    Listing Five

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
      @interface FooLink
    {
        FooLink *fooNext;
        __weak FooLink *fooPrev;
        NSData *fooData;
        NSInteger fooID;
    }
    @property(readwrite, assign) FooLink *nextLink;
    @property(readonly) FooLink *prevLink;
     
    - (NSData *)getData;
    - (void)addToLink:(NSData *)aDat;
    - (FooLink *)searchForLink:(NSInteger)anID;
    - (BOOL)hasLink:(NSInteger )anID;
    //...
    @end

    The __weak directive declares a reference that can be both weak and a nil. The alternative directive __unsafe_unretained declares one that is weak, but not a nil. Use this directive if you plan to zero out the references yourself. But be sure to do, or you will end up with a memory leak.
    There are other compiler directives, of course, but the aforementioned two are the ones you will use often.

  • Avoid recasting ObjC objects into C-pointers.
    Pointers to an ObjC object are of the generic type id. To use them as input to a C-routine, you could recast the pointer as shown by Listing Six (lines 3-9). Here, I used the factory method stringWithString: to create an NSString instance. Then I recast it into an integer pointer, and pass that pointer to the routine doFoo.

    Listing Six

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
      // Recasting an ObjC object pointer
    {
    id tStr;
    int *tPtr;
     
    tStr = [NSString stringWithString:@"foobar"];
    tPtr = (int *)tStr;
     
    doFoo(tPtr);
    }
     
    // Using a CFObject
    {
    CFStringRef tStr;
     
    tStr = CFSTR("foobar");
    doFoo(tStr);
    }

    However, recasting prevents ARC from managing the object correctly. ARC will not know when to retain the object and when to dispose of it. Moreover, the recast pointer could end up pointing to an invalid object. So, instead of recasting, use Core Foundation APIs to create a C-compatible object (lines 14-17).

  • Avoid using ObjC objects as fields in a C struct.
    We can use ObjC objects as fields inside a C struct. The sample struct in Listing Seven has two such fields (lines 4-5): one an instance of NSString, the other of NSURL. If the ObjC object is a custom one, we could render its properties visible with the @defsdirective.

    Listing Seven

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
      // A C struct with ObjC fields
    typedef struct FooStruct
    {
        NSString *fooName;
        NSURL *fooPath;
        int fooCount;
        char *fooData;
    } Foo;
     
    // A C struct with CoreFoundation fields
    typedef struct BarStruct
    {
        CFStringRef *barName;
        CFURLRef *barPath;
        int barCount;
        char *barData;
    } Bar;
     
    // The C struct rewritten as an ObjC class.
    @interface BarClass
    {
        NSString *fooName;
        NSURL *fooPath;
        int fooCount;
        char *fooData;
    }
    //...property accessors goes here
    @end

    But here too, ARC will be unable to manage an ObjC object inside a struct. It could not identify references made to that object and provide the right retainrelease, andautorelease statements for that object. Again, use Core Foundation APIs to create objects for a struct (lines 13-16). Or rewrite the struct as a simple ObjC class (lines 20-28).

  • Manage all C and CF objects yourself.
    Like garbage collection, ARC ignores all objects created using stdlib and Core Foundation APIs. It will not insert calls to malloc() and free(), which are needed to create and dispose of a struct or union. Nor will it insert calls to CFRetain() and CFRelease() to retain or dispose of a CF object.
    Make sure to supply these calls yourself. Take care to balance each call to malloc() with a call to free() and each call to CFRetain() with CFRelease(). Also make sure to avoid calls to free() or CFRelease() twice on the same object.

Conclusion

Automatic reference counting is an innovative way of managing ObjC objects on MacOS X 10.7 and iOS 5. It does away with explicit retainrelease and autorelease messages, and it cuts down potential memory leaks and null pointers. It offers optimized autorelease pools and it avoids the same runtime overhead as garbage collection. It even behaves the same on both platforms. However, there are aspects of ARC too complex to be covered in this space. To learn more about ARC, consult the references listed below.

References

Apple Developer. “Advanced Memory Management Programming Guide.” MacOS X Developer Library.https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.pdf[PDF].

 

Apple Developer. “NSGarbageCollector Class Reference.” Mac OS X Developer Library.https://developer.apple.com/library/mac/documentation/Cocoa/Reference/NSGarbageCollector_class/NSGarbageCollector_class.pdf[PDF].

Apple Developer. “Transitioning to ARC Release Notes.” iOS Developer Library.http://developer.apple.com/library/ios/releasenotes/ObjectiveC/RN-TransitioningToARC/RN-TransitioningToARC.pdf [PDF].

Apple Developer. “What’s New in Xcode.” MacOS X Developer Library.http://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/WhatsNewXcode/WhatsNewXcode.pdf[PDF].

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

Mike Ash. “Automatic Reference Counting.” http://www.mikeash.com/pyblog/friday-qa-2011-09-30-automatic-reference-counting.html.

Mugunth Kumar. “WWDC 2011, Session 323: Introducing Automatic Reference Counting.”http://blog.mugunthkumar.com/articles/migrating-your-code-to-objective-c-arc.

Paul Long. “Understanding Automatic Reference Counting in Objective C.”http://longweekendmobile.com/2011/09/07/objc-automatic-reference-counting-in-xcode-explained.

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s