ARC Rules

ARC Rules

To write and compile source code for ARC, you have to take care of a few things. Just by following the rules in the list below, you can write source code for an ARC-enabled environment with confidence.

  • Forget about using retain, release, retainCount, and autorelease.
  • Forget about using NSAllocateObject and NSDeallocateObject.
  • Follow the naming rule for methods related to object creation.
  • Forget about calling dealloc explicitly.
  • Use @autoreleasepool instead of NSAutoreleasePool.
  • Forget about using Zone (NSZone).
  • Object type variables can’t be members of struct or union in C language.
  • ‘id’ and ‘void*’ have to be cast explicitly.

The rules are explained one by one.

Forget About Using Retain, Release, RetainCount, or Autorelease

As you know, the compiler will perform memory management, therefore you don’t need to call memory management-related methods, such as retain, release, retainCount, and autorelease.

Apple says:

By enabling ARC with the new Apple LLVM compiler, you will never need to type retain or release again

But, in reality, you can’t use them. When you use them, a compile error occurs as follows.

error: ARC forbids explicit message send of 'release'
        [o release];
         ^ ~~~~~~~

So, In fact, Apple should say:

“By enabling ARC with the new Apple LLVM compiler, you can forget about the long hard days of typing retain and release.”

And, the retainCount and release methods also cause compile errors, thus the following code can’t be used with ARC.

for (;;) {
    NSUInteger count = [obj retainCount];
    [obj release];
    if (count == 1)
        break;
}

This source code does not fit into proper understanding of reference counting. So it should not be a problem. Anyway, retain, release, retainCount, and autorelease methods can be used only in a non-ARC environment.

Forget About Using NSAllocateObject or NSDeallocateObject

As you know, to create an object with ownership, you use an alloc method.

id obj = [NSObject alloc];

Actually, as we have seen in the implementation of alloc in GNUstep, in a non-ARC environment, it is possible to create an object and with ownership by calling NSAllocateObject function.2 With ARC, NSAllocateObject function can’t be used. It causes a compile error as if retain were used.

error: 'NSAllocateObject' is unavailable:
        not available in automatic reference counting mode

Also, NSDeallocateObject function can‘t be used.

Follow the Naming Rule for Methods Related to Object Creation

As explained in Chapter 1, there are naming rules for methods related to object creation with ownership.

  • alloc
  • new
  • copy
  • mutableCopy

When an object is returned by certain methods, where the name begins with one of the above list, the caller has ownership of the object. This rule is still applicable with ARC. Also, one more prefix is added along with it.

  • init

For any method where the name begins with init, there are some rules that are stricter than for the alloc/new/copy/mutableCopy method group.

The method has to be an instance method.

It has to return an object.

The return type has to be ‘id’ type, or types of its class, superclass, or subclass.

It returns an object without registering in autoreleasepool, as alloc/new/copy/mutableCopy method group does, which means the caller has ownership. Basically, it initializes an object, which is returned by alloc, and returns the same object as follows.

id obj = [[NSObject alloc] init];

__________

The above source code calls the alloc method to obtain an object and then calls the init method on the same object to initialize it. After that, init method returns the same object. Now, let’s see other examples.

- (id) initWithObject:(id)obj;

This method declaration meets the rules. Although the following method also meets the naming rule, it doesn’t return an object, so you can’t use it.

- (void) initThisObject;

Exceptionally, a method named “initialize” is not included in this init group. Therefore, you can use the following method as usual.

- (void) initialize;
Forget About Calling dealloc Explicitly

When all of the ownerships are released, the object will be discarded. It works just the same as a non-ARC environment. When disposed of, its dealloc method is called.

- (void) dealloc
{
     /*
      * Write here to be disposed of properly.
      */
}

For example, when you are using a library written in C and allocate a memory buffer, you need to free it in the dealloc method.

- (void) dealloc
{
    free(buffer_);
}

In many cases, dealloc is a suitable place to remove the object from delegate or observers.

- (void) dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

By the way, in a non-ARC environment, you have to type [super dealloc] every time as follows.

/* non-ARC */

- (void) dealloc
{
    /* Do something for this object. */

    [super dealloc];
}

With ARC, it causes a compile error, as when you call the release method.

error: ARC forbids explicit message send of 'dealloc'
        [super dealloc];
         ^     ~~~~~~~

You can’t call [super dealloc] explicitly. It is just done automatically by ARC. You can forget typing it again and again.

Use @autoreleasepool Instead of NSAutoreleasePool

As described in the previous section, you have to use the @autoreleasepool block instead of NSAutoreleasePool. If you use the NSAutoreleasePool class, a compile error occurs.

error: 'NSAutoreleasePool' is unavailable:
        not available in automatic reference counting mode
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
        ^
Forget About Using Zone (NSZone)

With ARC, Zone (NSZone) is not usable. As explained previously (see column “Zone”), regardless of whether ARC is enabled, the recent objective-C runtime (the environment in which compiler macro __OBJC2__ is defined) just ignores zones.

Object Type Variables Cannot Be Members of struct or union in C Language

When you add a member variable of Objective-C object type to C struct or union, a compile error occurs.

struct Data {
    NSMutableArray *array;
};


error: ARC forbids Objective-C objs in structs or unions
    NSMutableArray *array;
                    ^

Even with the LLVM compiler 3.0, there is no way to manage the lifetime of C struct because of the C language spec limitation.3 With ARC, the compiler has to know and manage an object’s lifetime in order to perform memory management. For example, automatic variables (local variables) can be managed because the compiler can know the lifetime as the scope of the variable. Because there is no such information for C struct members, it is impossible for the compiler to perform memory management for C struct. If you still want to put an object to C struct, you can do it by casting the object to“void *” (see next section) or by using an __unsafe_unretained ownership qualifier (see “Ownership qualifiers” section).

__________

3 LLVM.org “4.3.5. Ownership-qualified fields of structs and unions,”http://clang.llvm.org/docs/AutomaticReferenceCounting.html#ownership.restrictions.records

struct Data {
        NSMutableArray __unsafe_unretained *array;
    };

As described previously, the compiler doesn’t manage variables with the __unsafe_unretained ownership qualifier. You have to take care of ownership yourself to avoid a memory leak or the application will crash.

‘id’ and ‘void*’ Have to Be Cast Explicitly

In a non-ARC environment, casting from ‘id’ to ‘void*’ works without any problems as follows.

/* non-ARC */
id obj = [[NSObject alloc] init];
void *p = obj;

Also, it is no problem to call methods through ‘id’ variables, which are assigned back from void * as follows.

/* non-ARC */
id o = p;
[o release];

But these source codes cause a compile error with ARC.

error: implicit conversion of an Objective-C pointer
    to 'void *' is disallowed with ARC
        void *p = obj;
                  ^

    error: implicit conversion of a non-Objective-C pointer
        type 'void *' to 'id' is disallowed with ARC
        id o = p;
                ^

To cast between ‘id’ or object types and ‘void*’, you have to use a special kind of cast. Just for assignment, you can use __bridge cast.

__bridge cast

You can use __bridge cast as

id obj = [[NSObject alloc] init];

void *p = (__bridge void *)obj;

id o = (__bridge id)p;

With __bridge cast, you can cast ‘id’ to ‘void*’, and vice versa. But __bridge cast to void * is even more dangerous than an __unsafe_unretained qualified variable. You have to manage ownership of the object yourself carefully, or it crashes because of the dangling pointer. Also, there are two other kinds of casts: __bridge_retained and __bridge_transfer.

__bridge_retained cast

__bridge_retained cast works as if the assigned variable has ownership of the object.

id obj = [[NSObject alloc] init];
void *p = (__bridge_retained void *)obj;

The above source code can be rewritten for a non-ARC environment as follows.

/* non-ARC */

id obj = [[NSObject alloc] init];

void *p = obj;
[(id)p retain];

_bridge_retained cast has been replaced by retain. The variables “obj” and “p” have ownership of the object. Let’s see other examples.

void *p = 0;

{
    id obj = [[NSObject alloc] init];
    p = (__bridge_retained void *)obj;
}

NSLog(@"class=%@", [(__bridge id)p class]);

When leaving the scope of variable obj, its strong reference disappears and the object is released. Because variable p also has ownership, the object is not discarded. Let’s rewrite it for a non-ARC environment as usual.

/* non-ARC */

void *p = 0;

{
    id obj = [[NSObject alloc] init];
    /* [obj retainCount] -> 1 */

    p = [obj retain];
    /* [obj retainCount] -> 2 */

    [obj release];
    /* [obj retainCount] -> 1 */}


     /*
      * [(id)p retainCount] -> 1
      * which means,
      * [obj retainCount] -> 1
      * So, the object still exists.
      */

NSLog(@"class=%@", [(__bridge id)p class]);

Conversely, __bridge_transfer cast will release the object just after the assignment is done.

__bridge_transfer cast

You can use __bridge_transfer cast as

id obj = (__bridge_transfer id)p;

This source code can be rewritten for a non-ARC environment.

/* non-ARC */
id obj = (id)p;
[obj retain];
[(id)p release];

As __bridge_retained cast is replaced with retain, __bridge_transfer cast is replaced with release. The variable obj is retained because it is qualified with __strong. With these two casts, you can create, own, and release any objects without using ‘id’ or object type variables. But it is not recommended. Please be careful when you use them. Let’s see one more example.

void *p = (__bridge_retained void *)[[NSObject alloc] init];
NSLog(@"class=%@", [(__bridge id)p class]);
(void)(__bridge_transfer id)p;

This source code can be rewritten for a non-ARC environment as follows.

/* non-ARC */

id p = [[NSObject alloc] init];
NSLog(@"class=%@", [p class]);
[p release];

These casts are used frequently to convert Objective-C objects and Core Foundation objects.

OBJECTIVE-C OBJECT AND CORE FOUNDATION OBJECT

A Core Foundation object is an object used with the Core Foundation Framework. It is mainly written in C and has a reference count. In the Core Foundation framework, CFRetain and CFRelease are the equivalent functions to retain and release in Objective-C in a non-ARC environment.

The difference between a Core Foundation object and an Objective-C object is very small. It is more or less how it is created, either with Foundation framework (Cocoa) in Objective-C or with Core Foundation framework. After being created, it can be used transparently in both frameworks’ manner. For instance, even when an object is created with the Foundation Framework API, it can be released with the Core Foundation Framework API or vice versa.

Actually there is no difference between a Core Foundation object itself and an Objective-C object itself. So, to cast the object, in a non-ARC environment, just using a C language style cast is enough. This conversion is called a Toll-Free Bridge because just casting has no cost for the CPU.

You can see the list of Toll-Free Bridge classes in the following document.

Toll-Free Bridged Types
http://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFDesignConcepts/Articles/tollFreeBridgedTypes.html

For an ARC-enabled environment, to convert an object between Objective-C and Core Foundation, in other words, for a Toll-Free Bridge cast, the following functions are provided.

CFTypeRef CFBridgingRetain(id X) {
    return (__bridge_retained CFTypeRef)X;
}

id CFBridgingRelease(CFTypeRef X) {
    return (__bridge_transfer id)X;
}

Let’s see how we can use these functions.

CFBridgingRetain function

Listing 2–11 creates an NSMutableArray object with ownership and uses the object as a Core Foundation object.

Listing 2–11. CFBridgingRetain

CFMutableArrayRef cfObject = NULL;
{
    id obj = [[NSMutableArray alloc] init];
    cfObject = CFBridgingRetain(obj);
    CFShow(cfObject);
    printf("retain count = %d\n", CFGetRetainCount(cfObject));
}
printf("retain count after the scope = %d\n", CFGetRetainCount(cfObject));
CFRelease(cfObject);

The result is as follows. () means an empty array.

()
retain count = 2
retain count after the scope = 1

Now we’ve learned that an Objective-C object in the Foundation Framework can be used as a Core Foundation object. Also the object can be released with CFRelease. In the example, you can also use __bridge_retained cast instead of CFBridgingRetain as follows. Just choose whichever you like.

CFMutableArrayRef cfObject = (__bridge_retained CFMutableArrayRef)obj;

Let’s see the source code with comments on ownership status and the value of CFGetRetainCount (Listing 2–12).

Listing 2–12. CFBridgingRetain with comments

CFMutableArrayRef cfObject = NULL;
{
    id obj = [[NSMutableArray alloc] init];

     /*
      * variable obj has a strong reference to the object
      */
    cfObject = CFBridgingRetain(obj);

     /*
      * CFBridgingRetain works as if CFRetain is called and
      * the object is assigned to variable cfObject
      */

    CFShow(cfObject);
    printf("retain count = %d\n", CFGetRetainCount(cfObject));

     /*
      * Reference count is two.  One is for strong reference of variable obj,
      * The other is by CFBridgingRetain.
      */
}

 /*
  * Leaving the scope of variable obj, its strong reference disappears.
  * Reference count is one.
  */

printf("retain count after the scope = %d\n", CFGetRetainCount(cfObject));
CFRelease(cfObject);

/*
* Reference count is zero because of CFRelease.
* So, the object is discarded.
*/

Next, let’s see what will happen if __bridge cast is used instead of CFBridgingRetain or __bridge_retained cast (Listing 2–13).

Listing 2–13. __bridge cast is used instead of __bridge_retained cast

CFMutableArrayRef cfObject = NULL;
{
    id obj = [[NSMutableArray alloc] init];

     /*
      * variable obj has a strong reference to the object
      */

    cfObject = (__bridge CFMutableArrayRef)obj;
    CFShow(cfObject);
    printf("retain count = %d\n", CFGetRetainCount(cfObject));

     /*
      * __bridge cast does not touch ownership status.
      * Reference count is one because of variable obj's strong reference.
      */
}

 /*
  * Leaving the scope of variable obj, its strong reference disappears.
  * The object is released automatically.
  * Because no one has ownership, the object is discarded.
  */

 /*
  * From here, any access to the object is invalid! (dangling pointer)
  */

printf("retain count after the scope = %d\n", CFGetRetainCount(cfObject));
CFRelease(cfObject);

Now, we understand why CFBridgingRetain or __bridge_retained cast is needed.

CFBridgingRelease function

Listing 2–14 shows how to create an NSMutableArray object with a Core Foundation API in an opposite manner. It uses the CFBridgingRelease function.

Listing 2–14. Creating NSMutableArray object

{
    CFMutableArrayRef cfObject =
        CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
    printf("retain count = %d\n", CFGetRetainCount(cfObject));
    id obj = CFBridgingRelease(cfObject);
    printf("retain count after the cast = %d\n", CFGetRetainCount(cfObject));
    NSLog(@"class=%@", obj);
}

You can see that the object is created with ownership by the Core Foundation Framework API. As the opposite of the previous example, the object is used as an Objective-C object, The result is as follows.

retain count = 1
retain count after the cast = 1

Of course you can use bridge_transfer instead of CFBridgingRelease.

id obj = (__bridge_transfer id)cfObject;

And, let’s see the source code with comments on ownership status as usual (Listing 2–15).

Listing 2–15. Creating NSMutableArray object with comments

{
    CFMutableArrayRef cfObject =
    CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
    printf("retain count = %d\n", CFGetRetainCount(cfObject));

     /*
      * The object is created with ownership by Core Foundation Framework API.
      * The retain count is one.
      */

    id obj = CFBridgingRelease(cfObject);

     /*
      * By assignment  after CFBridgingRelease,
      * variable obj has a strong reference and then
      * the object is released by CFRelease.
      */

    printf("retain count after the cast = %d\n", CFGetRetainCount(cfObject));

     /*
      * Only the variable obj has a strong reference to
      * the object, so the retain count is one.
      *
      * And, after being cast by CFBridgingRelease,
      * pointer stored in variable cfObject is still valid.
      */

    NSLog(@"class=%@", obj);
}

 /*
  * Leaving the scope of variable obj, its strong reference disappears.
  * The object is released automatically.
  * Because no one has ownership, the object is discarded.
  */

Let’s see what happens when __bridge cast is used instead of CFBridgingRelease or __bridge_transfer cast (Listing 2–16).

Listing 2–16. __bridge cast is used instead of __bridge_transfer cast

{
    CFMutableArrayRef cfObject =
    CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
    printf("retain count = %d\n", CFGetRetainCount(cfObject));

     /*
      * The object is created with ownership by Core Foundation Framework API.
      * The retain count is one.
      */

    id obj = (__bridge id)cfObject;

     /*
      * variable obj has a strong reference because it is qualified with __strong.
      */

    printf("retain count after the cast = %d\n", CFGetRetainCount(cfObject));

     /*
      * Because variable obj has a strong reference and
      * CFRelease is not called,
      * retain count is two.
      */

    NSLog(@"class=%@", obj);
}

 /*
  * Leaving the scope of variable obj, its strong reference disappears.
  * The object is released.
  */

 /*
  * Because the reference count is one, it is not discarded. Memory leak!
  */

As shown in the above example, you have to use CFBridgingRetain/CFBridgingRelease or __bridge_retained/__bridge_transfer cast properly. When you need to assign Objective-C objects to variables of C type, such as void*, you have to implement it with the utmost care.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值