On Time, On Point, On Budget!

Garbage collector in iOS? No! Effective memory management using ARC. Part 1

Elisaveta Gordienko

Apple Inc. introduced a technology of automatic reference counting in 2011 and recommended ARC for using in new projects and also a transfer of existing code under ARC control.

However, not all developers are quick to change already familiar patterns of manual memory management with the introduction of new technology. One of the reasons is that ARC not only frees a developer from having to evidently send messages retain and release, but also requires more deep understanding and attention to relations and connections between the objects, proper approach to the code writing and also knowledge and understanding of some features, which will be noted in the following sections.

Before proceeding directly to the consideration of ARC, let’s consider the compiler’s structure providing this technology.

About compiler

LLVM compiler – is a compiler of new generation based on open source project LLVM.org.

Clang – is a front end LLVM for C-family languages.

In Xcode LLVM compiler uses front end Clang for source code analysis and its transfer to the intermediate format. Then code generator LLVM (back end) transfers the code from intermediate format into machine code. Xcode also contains LLVM GCC compiler that uses front end GCC compiler for the maximum compatibility and back end LLVM that uses the advantages of the best code generator LLVM.

Xcode uses a modified version of GNU Compiler Collection, and also starting from Xcode 3.1, LLVM-GCC compiler with front ends from GNU Compiler Collection and code generator based on LLVM, and starting from Xcode 3.2, Apple LLVM Compiler with front end Clang and code generator based on LLVM as well as Clang Static Analyzer.

Using Apple LLVM the code is compiled two times faster than when using GCC. The resulting applications also work faster. [1, 4]

Clang Static Analyzer is really useful tool for searching of errors in the code related to memory management. But if the analyzer can find such errors then why can’t it also fix them instead of a developer?

This is the main point of ARC: compiler doesn’t use the built-in memory management rules for detecting of the developer’s errors, but just take responsibility for code completion with necessary calls.

Automatic Reference Counting (ARC)

ARC implements an automatic memory management for Objective-C objects and blocks, releasing a developer from having to evidently send messages retain and release, but at the same time requires more attention to relations and connections between the objects, and proper approach to the code writing.

Using of automatic memory management allows making the application development more productive (see figure below), simplify further code maintenance and provide greater stability of applications.

ARC doesn’t provide cycle collector that requires an explicit control of objects’ lifetime and breaking of retain cycles manually or using weak and unsafe references.
img1

Appearance

ARC is supported with Xcode 4.2, Mac OS X 10.6 “Snow Leopard” and iOS 4.0, but only with Mac OS X 10.7 “Lion” and iOS 5 all ARC opportunities are available (in OS X v10.6 and iOS 4 weak references are not supported).

ARC technology should replace garbage collection in Mac applications starting from OS X Mountain Lion v10.8. [2]

ARC is a next step of Objective-C evolution that allows making the development more productive, the code more safe, the applications more stable. Regarding this using of ARC and transfer of existing code under the control of ARC is recommended by Apple.

Support

ARC is activated by the compiler’s flag -fobjc-arc (turned off by flag -fno-objc-arc). In one project can be combined classes with manual and automatic memory management.

If ARC is activated, __has_feature(objc_arc) is converted by preprocessor into 1.

Briefly about mechanism

ARC is not a garbage collector. This means that scanning of the heap doesn’t occur, there are no pauses related to this when the application is run, and there are no nondeterministic release calls.

ARC doesn’t automate malloc/free, as well as control of pointers of CF type or any other retainable C pointers. Because actually ARC can’t even distinguish such types from the ordinary C pointers [3 7.8].

On the compilation step the code is completed with inserts providing exactly that lifetime of the objects, which is required. And the same agreements are applied as with a manual memory management.

In order to allow the compiler to generate the correct code ARC applies the restrictions on using of methods for memory management and toll-free bridging, and also introduces new descriptors of properties and object pointers.

Xcode provides a tool for conversion of existing code into ARC. Some transformations converter performs without user participation (for example, deletion of retain and release calls, changing of descriptors of properties and object pointers), and also helps to fix problems that can’t handle automatically. Under the ARC control can be transferred both a whole project and individual files.

Does ARC influence on the speed of compilation and work?

  • no overhead GC costs
  • improvement of performance: NSObject retain/release is 2.5 times faster, @autoreleasepool is 6 times faster, objc_msgSend is on 33% faster, retain/autorelease returns is 20 times faster

Compiler effectively eliminates many unnecessary retain/release calls and speed up Objective-C runtime in general. In particular, a standard “return a retain/autoreleased object” pattern is much faster and doesn’t really place the object into autorelease pool, when method is called from ARC code.

You should remember that the optimizer doesn’t work in general debug configuration, so there will be more retain/release traffic under -O0, than –Os.

ARC works also in ObjC++ mode.

New rules

When using ARC:

  • explicit dealloc call is prohibited, however, realization of dealloc is allowed when it is necessary to make some actions except releasing of instance variables (for example, [self setDelegate:nil]);
  • realization and call of release, retain, autorelease, retainCount is prohibited;

There are several reasons which cause the need in custom retain and release implementations:

  • Performance: implementation of retain and release for NSObject has become now much more efficient and fast therefore custom implementation is no longer needed.
  • Implementation of the system of custom weak pointers: now instead of this you should use __weak.
  • Implementation of singleton: now is implemented using shared instance pattern. Also you can use class instead of instance methods, which allows avoiding the need to give an object to all.
  • you can still use CFRetain, CFRelease and other when working with Core Foundation-style objects because of the absence of the possibility of full CF objects management automation; [3 3.3.2]
  • Using of NSAllocateObject and NSDeallocateObject is prohibited, objects are created using alloc;
  • Regarding blocks, when using callback via return (up the stack) there is no need to call Block copy. However when passing as a parameter (down the stack), for example, in arrayWithObjects: and other methods executing retain it’s still necessary to use [^{} copy]. You should remember that NSString * __block myString is retained in ARC mode (is not a hanging pointer). To get a behavior as with manual memory management you should use __block NSString * __unsafe_unretained myString or better use __block NSString * __weak myString;
  • Using of object pointers in C structures is prohibited [3 p.4.3.5 – comments and explanations]

The following code is not compiled:
struct X { id x; float y; };

The reason: by default x is strong, and compiler can’t safely synthesize all code necessary for correct work. For example, if you transfer the pointer to such structure using code ending with free call, each id should be released before free for struct will be called. The compiler can’t do it safely, so strong id in structures are completely prohibited in ARC mode. But there are several possible solutions:

  • to use Objective-C objects instead of structures (this is the best solution)
  • if using of Objective-C objects is not optimal, then it is recommended to use void*. This requires using of explicit casts
  • to mark the references to the objects __unsafe_unretained. This approach can be useful in patterns of the following type:
    struct x { NSString *S; int X; } StaticArray[] =
    {
    @"first", 1,
    @"second, 2,
    ...
    };

    Structure is defined as follows:
    struct x { NSString * __unsafe_unretained S; int X; }
    This approach is not safe, but for the objects existing during the lifetime of the application (for example, for string literals) may be very useful.

  • for id and void* now you should use special casts, which give a compiler the information about lifetime of the objects (the need appears when converting Objective-C objects and Core Foundation types);
  • you can’t use NSAutoreleasePool objects; instead ARC provides @autoreleasepool blocks, using of which is more effective; [3 7.2]
  • you can’t use memory zones; in principle, there is no need to use NSZone, because modern Objective-C runtime will still ignore them;
  • For compatibility with MRR the restrictions are applied to the names of methods: for example, you can’t start the name of accessor from “new” and therefore give such names for properties not redefining the getters;
    // Not work:
    @property NSString *newName;
    // Work:
    @property (getter=theNewName) NSString *newName;
  • it is required that the result [super init] was assigned to self in init methods;
  • “assigned” instance variables become strong

Before ARC the instance variables were non-owning references – the direct assignment of the object to instance variable didn’t increase the lifetime of the variable. In order to make strong property accessor method was usually implemented, calling the proper memory management method. On the contrary, to support weak properties, accessor could be defined as follows:

@interface MyClass : Superclass
{
id thing; // Weak reference.
}
// ...
@end
@implementation MyClass
- (id)thing
{
return thing;
}
- (void)setThing:(id)newThing
{
thing = newThing;
}
// ...
@end

In ARC instance variables by default are strong references – the assignment of the object to instance variable increases the lifetime of the object.

To maintain the old behavior you should mark the instance variable as weak or use definition of the property.

@interface MyClass : Superclass
{
id __weak thing;
}
// ...
@end
@implementation MyClass
- (id)thing
{
return thing;
}
- (void)setThing:(id)newThing
{
thing = newThing;
}
// ...
@end

Or:

@interface MyClass : Superclass
@property (weak) id thing;
// ...
@end
@implementation MyClass
@synthesize thing;
// ...
@end

Sources:

  1. www.llvm.org
  2. developer.apple.com/library/ios/#releasenotes/ObjectiveC/RNTransitioningToARC/Introduction/Introduction.html
  3. clang.llvm.org/docs/AutomaticReferenceCounting.html
  4. developer.apple.com/library/ios/#documentation/CompilerTools/Conceptual/
    LLVMCompilerOverview/_index.html

 

This entry was posted on Tuesday, April 23rd, 2013 at 8:15 am and is filed under iPhone.