Hi fellows!

This article will treat article the myth and truths behind iOS ARC (Automatic Referece Counting) and MRR (Manual Retain Release). Let's understand how it works, what the compiler does with it, why some people love it and other hate it. Also, my favorite part, let's see how to create a cross ARC/MRR application! When working on team, usually in agencies and companies we have a Senior developer (which usually prefers MRR) and a Junior developer (which prefers ARC) working on the same project. Using this technique both can work happy together.


At a glance

It's important to say that this is not an ARC tutorial. I'll not teach you how to use ARC at all. To continue reading you must already know what ARC is. This article is to show you how to integrate ARC and MRR in the same application, or is better to say, how to make a code that compiles in ARC and in MRR environment without changing one single line of code.

First off, we must understand how the ARC goes on, what it really means to the final compiled code. Of course, if you're a Senior dev, you can just skip this introduction and go to the funny part here.

Here is the sample Xcode project for this tutorial:
Download Xcode project files to iPhone
Download now
Xcode project files to iOS 5.1 or later
4.0Mb


Github: https://github.com/dineybomfim/arc-mrr/

OK. Let's get started.


The ARC concept

The first thing you must to understand about ARC is: it's sucks! The ARC concept to save your time coding is not really a big deal for who really knows to manage the memory of the application. In fact, there is no real time save using ARC, the point about it is: for developers that came from other languages that support Gargabe Collection, the memory management concept is really a very hard part to understand.

So here is the real point of Apple creating ARC: learning Obj-C for those ones that never saw any kind of memory management is really painful!

I can say that because I walked through this path. I started my developer life with very high ending languages, Server Side languages, Java Script and this kind of shit. So when I meed C the concept of memory management was something really new with no relation with my background. Thinking like Apple, increase the group of Obj-C developer is very important and then ARC concept comes in to solve a big hole in the issue about engaging new developers, increasing the community.

Some may ask about Garbage Collection. Well, Mac OS has Garbage Collection and MRR as well. But for iOS, running on very tiny and limited devices the life battery matter is the biggest problem. Garbage Collection on iOS could really kill the battery life so this is completely out of the question. Running something on background all the time just to make sure the memory management is on the track is not possible on mobiles.

The ARC concept is very simple: developers don't need to think about the memory management because we (Apple) will review his code, looking for all the places where the memory should be allocated and released. Then we (Apple) will place the instructions about retaining, releasing or even autoreleasing in the right spots. All this job will be made during the compilation of the code. So every time the developer hit "compile" we'll review his code first. No costs for the application on runtime. It's a very great idea, doesn't it?

Yeah, the theory is perfect, but not the practice. Memory management is something extremely complex and has a lot of collateral effects in the entire code. So to make sure the Apple ARC is not doing shit, they ask to the developer to give some instructions, like saying this variable is "strong", that means, I'll need it over a very long time and this variable is "weak", that means, I just need it for a short period of time.

There are much more. The developers also need to say when a basic C variable is cast to an Obj-C variable what kind of conversion is involved, because the ARC is just for Obj-C, not for pure C.
O.o
Oh God...

The high Obj-C framework is far for controlling everything. If you need anything more specific you'll face the low level Apple frameworks, that is pure C. Actually I love C. I'm used to say that here is no real iOS or Mac application made with pure Obj-C. You'll always mix Obj-C and pure C, because in fact Obj-C is a C (a superset of C).

So, I think you already got my point. In very simple questions: Apple had a good intention creating ARC. However this is just for beginners. If you want to make a little bit better application you'll need to learn a lot of concepts of memory management. You'll need to learn about C pointers and MRR any way. Even if you choose for keep using ARC, you'll need to learn how it really works.


ARC - Behind the scenes

OK, no more shit talking, let's go to the action. Let's understand what goes behind the ARC.

ARC code

@interface ClassA : NSObject
{
@private
    NSDictionary *_myDict;
}

- (void) myMethod;
@end

@implementation ClassA

- (void) myMethod
{
    NSMutableArray *array = [[NSMutableArray alloc] init];

    unsigned int i, length = 10;
    for (i = 0; i < length; ++i)
    {
        [array addObject:[[NSString alloc] initWithFormat:@"%i",i]];
    }

    _myDict = [[NSDictionary alloc] initWithObjectsAndKeys:array, @"keyArray", nil];
}

@end

The above is what you code, but this is what really goes on to the compiler:

TRUE ARC code

@interface ClassA : NSObject
{
@private
    __strong NSDictionary *_myDict;
}

- (void) myMethod;
@end

@implementation ClassA

- (void) myMethod
{
    NSMutableArray *array = [[NSMutableArray alloc] init];

    unsigned int i, length = 10;
    for (i = 0; i < length; ++i)
    {
        NSString *__scopeVar1 = [[NSString alloc] initWithFormat:@"%i",i];
        [array addObject:__scopeVar1];
        [__scopeVar1 release];
        __scopeVar1 = nil;
    }

    if (_myDict != nil)
    {
        [_myDict release];
        _myDict = nil;
    }
    _myDict = [[NSDictionary alloc] initWithObjectsAndKeys:array, @"keyArray", nil];

    if (array != nil)
    {
        [array release];
        array = nil;
    }
}

- (void) dealloc
{
    if (_myDict != nil)
    {
        [_myDict release];
        _myDict = nil;
    }

    [super dealloc];
}

@end

WOW, much more code, it's true! However it's much more understandable to see what is going on with the application memory.

Notice that variables inside a scope will always die, I mean, be released inside the same scope. The "array" for example will die in the end of its scope and the "__scopeVar1" will die in the end of its own scope. It's important to say that the ARC will always prefer the direct "release" instead of "autorelease", because it's better, fasters and saves more memory. In fact, the "autorelease" is used in very few situations by ARC, like the returning of a function/method, in this case there is no other way than autoreleasing the instance. Let's see more about "autorelease" soon.

Other thing I want to call the attention is how ARC will release the variables. ARC is prepared to use what we call "safe release", that means, the variable will always be set to "nil" after being released. This is a safety way to avoid Zombies. Don't know what is it? Zombies is where an instance receives a double "release" command. So if an instance was already released and receives another "release" message the application will crash. I'll explain it better right next, now just keep in mind that by checking if the variable is not "nil" and then setting it to "nil" right after releasing it is a safe way to avoid Zombies. Of course there are many other ways to get a Zombie, however by using this method you can avoid a lot of them.

More about ARC in my Obj-C Memory article.


ARC + MRR = S2

OK fellows, here is what you're looking for. The problem today is, like I said, the team work, when a senior developer wants to use MRR and a Junior just know about the ARC. I like to create a global file called Runtime.h, it will hold all the runtime related things, in majority, C macros.

Here is what you need.

Runtime.h

/*
 *    ARC+MRR.h
 *    ARC+MRR
 *    
 *    Created by Diney Bomfim on 4/28/13.
 *    Copyright 2013 db-in. All rights reserved.
 */

// Defines the ARC instructions.
#if __has_feature(objc_arc)

    // ARC definition.
    #define IS_ARC

    // Convertion instructions.
    #define ARC_UNSAFE          __unsafe_unretained
    #define ARC_BRIDGE          __bridge
    #define ARC_ASSIGN          __weak
    #define ARC_RETAIN          __strong

    // Property definitions
    #define RETAIN              strong
    #define ASSIGN              weak
    #define COPY                copy

#else

    // Convertion instructions.
    #define ARC_UNSAFE
    #define ARC_BRIDGE
    #define ARC_ASSIGN
    #define ARC_RETAIN

    // Property definitions
    #define RETAIN              retain
    #define ASSIGN              assign
    #define COPY                copy

#endif

// The retain routine.
#ifdef IS_ARC
    #define arcRetain(x)        (x)
#else
    #define arcRetain(x)        ([x retain])
#endif

// The release routine.
#ifdef IS_ARC
    #define arcRelease(x)       ({ (x) = nil; })
#else
    #define arcRelease(x)       ({ if(x) { [x release]; (x) = nil; } })
#endif

// The autorelease routine.
#ifdef IS_ARC
    #define arcAutorelease(x)   (x)
#else
    #define arcAutorelease(x)   ([x autorelease])
#endif

// The free routine, not really necessary for ARC, but let's do it to make a safe free as well.
#define nppFree(x)              ({ if(x) { free(x); (x) = NULL; } })

That's it!
I prefer to use the terms "RETAIN" and "ASSIGN", because they sounds more correctly to me. Of course you can change it to "STRONG" and "WEAK" respectively if you want. Anyway, the point it how to use it? Here:

Using the Runtime.h

@interface ClassA : NSObject
{
@private
    NSDictionary *_myDict;
    ARC_ASSIGN NSString *_tempVar;
}

@property (nonatomic, RETAIN) NSString *strongProperty;
@property (nonatomic, ASSIGN) NSString *weakProperty;
@property (nonatomic, COPY) NSString *copyProperty;

- (void) myMethod;
@end

@implementation ClassA

- (void) myMethod
{
    NSMutableArray *array = arcAutorelease([[NSMutableArray alloc] init]);

    unsigned int i, length = 10;
    for (i = 0; i < length; ++i)
    {
        NSString *__scopeVar1 = [[NSString alloc] initWithFormat:@"%i",i];
        [array addObject:__scopeVar1];
        arcRelease(__scopeVar1);
    }

    arcRelease(_myDict);
    _myDict = [[NSDictionary alloc] initWithObjectsAndKeys:array, @"keyArray", nil];
}

@end

There are other usages of these definitions. I'll not show all here to don't take too much of your time. In this tutorial there is a sample project, you can get it and see all the other usages. Check how it's simple to use it. You can turn OFF or ON the Objective-C ARC compiler's feature without changing even one single line of code!


Conclusion

Very well my friends, that's it. Let's make a fast concept review:

  1. ARC is not a savior! It's just a way that Apple founds to avoid loosing near devs.
  2. ARC just make a very simple work during the compilation, not a big deal.
  3. Using macros we can easily "undo" what ARC "does". This is good for large team works and specially when making frameworks.

Remember, ARC will not save your ass all the time. You must understand what goes under the code to avoid leaks and zombies. I've seen junior developers getting zombies even with ARC! Trust me, it's much more easy than you can image.

If you have any doubts, just Tweet me:

See you soon!

Download Xcode project files to iPhone
Download now
Xcode project files to iOS 5.1 or later
4.0Mb


Github: https://github.com/dineybomfim/arc-mrr/

© db-in 2017. All Rights Reserved.