Objective-C Memory Management Essentials
上QQ阅读APP看书,第一时间看更新

How ARC looks

Start by picturing a traditional Objective-C source code file written by an expert Cocoa programmer. The retain, release, and autorelease messages are sent in all the right places and are in perfect balance.

Now, imagine editing the source code file, removing every instance of the retain, release, and autorelease messages, and changing a single build setting in Xcode that instructs the compiler to put all the suitable memory management calls back into your program when the source code is compiled. That's ARC. It's just what the name suggests—traditional Cocoa reference counting, being automatically done.

At its core, ARC is not a runtime service; it doesn't work on program execution, as Garbage collection does. On the other hand, the new Clang, the compiler frontend for C, C++, Objective-C, and Objective-C++, provides it as a two-part phase (we will call these phases "cycles"). In the following diagram, you can see these two phases. At the cycle named frontend as shown in the following diagram, Clang will analyze every preprocessed file for properties and objects. And then, relying on a few fixed rules, it will insert the correct statements—retain, release, and autorelease.

How ARC looks

For instance, if an object is allocated and locally corresponds to a method, this object will have a release statement close to that method's endpoint. This release statement, if it is a class property, comes into the dealloc method in a class, which can be your custom class or any Objective-C class. If it's a collection object or a return value, it will get an autorelease statement. However, if it was referenced as weak, it will be left in peace.

The frontend also inserts retain statements for disowned objects locally. It goes to every declared accessor and updates them with the directive @property. It includes calls to the dealloc routine of their superclasses such as NSObject or UIViewController or even your own customer superclass. It will also report any explicit management call and double ownership.

In the optimize cycle, the modified sources are sent to load balancing by Clang. So, it calculates the retain and release calls created for each object, and reduces all to the optimal minimum. This action avoids excessive retain and release messages with the possibility to impact with full performance:

To see how it works, take a look at the following code:
@class MyBar;
@interface MyFoo
{
@private
    NSString *myOwnString;
}
@property(readonly) NSString *myOwnString; 

- (MyBar *)getMyBarWithString:(NSString *)myString;
- (MyBar *)getMyBar;

@end


@implementation MyFoo;
@dynamic myOwnString;

– (MyBar *)getMyBarWithString:(NSString *)myString
{
    MyBar *yBar;

    if (![self.myString isEqualToString:myString]) 
    {
        myOwnString = myString;
    } 
    return [self getMyBar];
}

- (MyBar *)getMyBar
{
    MyBar *yBar

    return yBar;
}
@end

Now, it's an Objective-C class with no retain or release. There is one private property named myOwnString, which is an instance of NSString. This class imports the header of the MyBar class (line 1) and declares a read-only getter with the same name, myOwnString. There is a modifier called getMyBarWithString and an internal function named getMyBar.

The following code is the same piece of code using Manual Reference Counting (MRC):

@class MyBar;
@interface MyFoo
{
@private
    NSString *myOwnString;
}
@property (readonly) NSString *myOwnString; 

- (MyBar *)getMyBarWithString:(NSString *)myString;
- (MyBar *)getMyBar;

@end


@implementation MyFoo;
@dynamic myOwnString;

– (MyBar *)getMyBarWithString:(NSString *)myString
{
    MyBar *yBar;

    if (![self.myString isEqualToString:myString]) 
    {
        [myString retain];
        [myOwnString release];
        myOwnString = myString;
    }
    return [self getMyBar];
}

- (MyBar *)getMyBar
{
    MyBar *yBar

    [yBar autorelease];
    return yBar;
}

- (void)dealloc
{
    [myOwnString release];
    [super dealloc];
}
@end

Note that the class interface is still the same. However, now, the getMyBarWithString modifier has some new statements; more specifically, two:

[myString retain];
[myOwnString release];

Sending a release statement to the myOwnString property (line 24) is the responsibility of one of them. The other sends a retain message to the myString argument (line 25). Before returning the last one as its result, the getMyBar function sends locally a autorelease statement to the yBar local. Lastly, MRC supersedes the dealloc method of that class. MRC also releases the myOwnString property (line 44) and invokes the dealloc method of its superclass (line 45); still in that method, if there is already a dealloc method, MRC properly updates its code.

When using ARC, you don't need to explicitly insert retain and release messages, as ARC will automatically insert them during compilation. Since ARC decides by itself how an Objective-C object will be better managed, the time that will be required to develop the class code is not required anymore. So, ARC avoids any empty pointers. ARC can also be excluded on a per-file basis where you select your target, go to Build Phases, and add the -fno-objc-arc flag in Compiler Flags.

However, the Clang compiler is built into LLVM 3.0, only available on Xcode since version 4.2. There has been optimized runtime support for ARC ever since Mac OS X Version 10.7 and iOS Version 5.0. It is not challenging to use ARC with binaries from Mac OS X 10.6 and iOS 4.3, but for iOS 4.3, it's only achievable through blue code; and for OS X 10.6, the newest version does not make use of weak pointers at all.

Some points about ARC are as follows:

  • It does not work with AppleScriptObjC or even PyObjC sources; it works exclusively with Objective-C sources.
  • However, more or less, when there are PyObjC and AppleScriptObjC classes being connected to Cocoa by Objective-C code, ARC will affect that underlying code.
  • Note that for some third-party frameworks, if ARC is enabled, they might crash while compiling. Ensure that the developer of such a framework can and will update it.