One of the recent improvements introduced with Xcode 4′s new LLVM Compiler 3.0 is Automatic Reference Counting (ARC). ARC is a compiler feature that will automatically manage the traditional iOS retain/release memory management model. When you enable ARC for a project, you simply do not ever call retain or release – the compiler figures it out for you. More to the point, when using ARC you are not allowed to use the retain/release calls at all, making older code incompatible with ARC compilation settings. There are already some great tutorials on ARC out there, so I’m not going to go into the how’s and why’s of ARC itself.
So What’s the Problem?
Now ARC is quite the cat’s pajamas as they say, but it does have some limitations. Mainly, it’s not immediately obvious how you can make use of older code that was not written with ARC in mind. Say you create a shiny new ARC-enabled project, and then find out that a shared component you want to use, the Facebook SDK for example, isn’t yet ARC enabled and won’t compile. You have to tell the compiler which source files are ARC ready and which aren’t. There in lies the rub: you can convert your old code to use ARC for sure, as I’ll discuss below, but when you are using third party components this isn’t always practical or desirable, as you’ll have a much harder time taking updates to those libraries.
I’m a big fan of using third party modules from the open source community, mostly from github. I like to keep these in git submodules so that I can easily take and contribute updates to them. Forking the code and trying to convert it to ARC for my own personal usage breaks the update/contribute cycle.
What I’m going to explain in this post is how you can use non-ARC code libraries inside an ARC-based project with a minimum of fuss and bother by using a static library to separate your ARC and non-ARC code without you having to modify the older code in any way.
Doing Things The Hard Way
First, let me quickly cover some alternatives to the static library approach. In some circumstances, these approaches are reasonable and convenient. When dealing with larger code libraries however, the static library approach is a real time saver.
Compiler flags to the rescue! For each source file that is non ARC compliant, add the –fno-objc-arc compiler flag. Nothing more fun then managing compiler flags. This is the way I’ve seen most ARC documentation explain how to make use of non-ARC code in an ARC project, or vice-versa. These can be set on individual files by selecting the target in Xcode, going to the Build Phases screen, and double clicking on the source files row in the Compile Sources phase. Then, enter the magic compiler flag into the box. Repeat for all your source files. Fun, eh?
Using the ARC Migration Wizard
If you have a larger batch of code to use, you can use the ARC migration wizard to convert all of the code in a specific target to ARC by selecting Convert to Objective-C ARC from the Edit | Refactor menu. You’ll still probably have to clean up a few things, but this is a convenient way to port your code to ARC. The problem is that if you are doing this with a third party library, such as an SDK of a public domain component then you are making changes to the actually code. This will make things tougher in the future if you want to upgrade the library. You are also making untested changes to the code base, opening the possibility for new bugs or unexpected behavior. Using the static library approach described below, however, you can leave this code unmodified.
Creating a non-ARC Static Library
The basic approach will be to add a second target to our project which is a static library. This library will hold all of our non-ARC code. The library will be built automatically with your main target and linked against it when you build, so you will not need to do anything special to build or distribute the application in this approach. A static library essentially lets you have two sets of compilation settings – one for your fancy new ARC code, and another set for all of your old code.
Note: The sample project I’m talking through here is available for download on github.
1. Create your Static Library Target
Select your project file in Xcode, and click on the Add Target button at the bottom of the Project Settings screen. When prompted, select the Cocoa Touch Static Library template from the Framework and Library section and click Next.
For the project name, enter whatever you want your library to be called. In this case I’m going to call my library ArcFree, but it doesn’t really matter. Click Finish. Make sure that the “Use Automated Reference Counting” option is not selected. This will create a second target in your project that can be built right along side the first.
Delete the sample class the wizard created (ArcFree.m, ArcFree.h) as you won’t need them. You’ll notice it creates a precompiled header file as well. This can be useful, but if you don’t think you need it you can delete it – but you’ll have to modify the target’s build settings, which have been set to look for that file.
2. Verify that the Static Library is Not Using ARC
Now we have to fix what I suspect is a bug in Xcode 4.2. Go to your static library target’s Build Settings and search for “automatic” so that we can take a look at the Objective-C Automatic Reference Counting setting. Despite the fact that we told Xcode that we didn’t want ARC when we created the library, it doesn’t set an override on the target so this setting is inherited from the main project. Set this to “No” for your static library.
I believe what is happening is that the Xcode wizard asks about ARC so that it can setup the target’s project file correctly, but since we are adding a target to an existing project it doesn’t actually do anything. In any case, setting this flag to No means that any files compiled as part of this target will not use ARC – they will use the classic retain/release model. Verify that the flag is set correctly.
3. Link with Your Static Library
To use any of the classes in your static library you must link it to your main application target. This is just like adding system libraries and frameworks, just with your own static library instead of a dynamic one provided by the system.
Select your main application target, then click on Build Phases and expand the Target Dependencies section. Click on the plus(+) button and select your static library from the list. This tells the linker to build your static library’s classes into your main target after compiling them.
4. Add your Static Library as a Dependency
Select your main application target, then click on Build Phases and expand the Link Binaries with Libraries section. Click on the plus(+) button and select your static library from the list. This tells Xcode that when you build your main project, it needs to build the static library first (if it’s had any changes).
Adding Code to the Static Library
When you add code to the project, you will have the option of selecting which targets the code belongs to, as shown in the screen shot below. When you are using code that doesn’t support ARC, simply choose to place it into the static library, while ARC code can go into your main project. As an example, I’ll add the Facebook SDK to my example project.
First, I checkout the Facebook submodule in git.
Duanes-iMac:ArcLibraryExample duane$ mkdir submodules
Duanes-iMac:ArcLibraryExample duane$ git submodule add https://github.com/facebook/facebook-ios-sdk.git submodules/facebook-ios-sdk
Cloning into 'submodules/facebook-ios-sdk'...
remote: Counting objects: 964, done.
remote: Compressing objects: 100% (500/500), done.
remote: Total 964 (delta 525), reused 818 (delta 413)
Receiving objects: 100% (964/964), 1.83 MiB | 928 KiB/s, done.
Resolving deltas: 100% (525/525), done.
Next, I create a “Facebook” group, under the ArcFreeLibrary project tree. This is optional since project folders don’t have any net effect, but I like to keep things organized. Then, we add the required Facebook and JSON files to the project, and make sure that only the ArcFree target is selected as a target.
Build Your Project
Build your project normally and you should be good. Because you’ve setup a dependency between your main application target and the static library, your library is automatically compiled when required. Since you have multiple targets, you can also change your build target to the static library if you just want to build it stand alone, which can be useful when you are debugging.
If you get errors such as “release” is unavailable or “ARC forbids explicit message…” then double check the Objective-C Automatic Reference Counting build setting. This means it’s trying to build your target with ARC enabled. Also, verify that the non ARC source files are ONLY in the ArcFree library target, and not the main target as well.