Convert a project to ARC

Recently had to convert a gnarly old project to ARC. Here’s how I got on:

1. first, created a git branch

2. Xcode > Edit > Refactor > Convert to Objective-C ARC

and got “Cannot Convert” with 51 issues!

Back to the drawing board.

3. Read this:

http://stackoverflow.com/questions/9018366/how-to-enable-disable-arc-in-an-xcode-project

and

http://www.learn-cocos2d.com/2011/11/everything-know-about-arc/

Summarising this last page which has tons of information:

i. use >= iOS5.0

ii. use LLVM 3.0 or greater (currently 4.2) – note, if you’re Converting then this will be taken care of

iii. build 3rd party libraries separately as a static library or, less appealing, disable ARC for individual files with -fno-objc-arc (see later)

Here’s what i did:

a. Make sure I was only Converting my main Target

b. I was using ShareKit which was generating loads of issues. But I was using it in a pretty minor way so removed it and added a comment to come back to it

c. An old file I was using for reference had issues. I removed it from Build Phases > Compile Sources

d. Remove my lines of code that used retain, retainCountrelease, autorelease

e. Added -fno-objc-arc for 3rd party files that used retain, retainCountrelease, autorelease – and, in the headers of these files, added //TODO – update this file for ARC

Note: to disable ARC on multiple files, select all your files, press Enter, type in -fno-objc-arc and then click off the pop up once done.

Guide: http://stackoverflow.com/questions/6646052/how-can-i-disable-arc-for-a-single-file-in-a-project

f. Ran Xcode > Edit > Refactor > Convert to Objective-C ARC

Note that you need to make sure you’ve deselected files from the Target you want to keep ARC free.

Why?

‘cos “Convert to Objective-C ARC” is a code rewriting tool whereas -fno-objc-arc is a compile time flag. It’s irrelevant for the purpose of refactoring.

See this post:

http://stackoverflow.com/a/15029714/343204

g. A final obstacle – I was using Crashlytics which runs a Build Phase script. Select “Run script only when installing” to disable.

And finally, I got to this:

Screen Shot 2013-04-11 at 11 Apr  13.13.06http://www.snowcrash.eu/wp-content/uploads/2013/04/Screen-Shot-2013-04-11-at-11-Apr-13.13.06-588x397.png 588w, http://www.snowcrash.eu/wp-content/uploads/2013/04/Screen-Shot-2013-04-11-at-11-Apr-13.13.06.png 727w" sizes="(max-width: 300px) 100vw, 300px" />

So, I created a final git branch – just so I could roll back and clicked Next.

Now, the code was converted. But it wouldn’t build complaining about:

Undefined symbols for architecture i386:
“_OBJC_CLASS_$_MyOldViewController”, referenced from:
objc-class-ref in ANewViewController.o
ld: symbol(s) not found for architecture i386
clang: error: linker command failed with exit code 1 (use -v to see invocation)

What was happening here was I had removed the file (MyOldViewController) from Targets > Build Phases > Compile Sources BUT still had the file included and referenced in ANewViewController.

More here:

http://stackoverflow.com/questions/6984368/undefined-symbols-for-architecture-i386-objc-class-skpsmtpmessage-refere

I just needed to remove it from these files.
And bingo, it built and ran!

Note, here’s a review of some of the changes that were made:

1. retain was replaced with strong

2. Where I had an NSAutoReleasePool, the relevant bit of code was wrapped with @autoreleasepool {}

3. references to retain, release, autorelease were removed

4. calls to super’s dealloc were removed. Note, dealloc methods themselves aren’t removed but in most cases can be removed. See the separate post on dealloc methods in ARC.

5. where I had references to my Delegate, an  __unsafe_unretained modifier was added

6. where I was casting to an NSString after using functions like CFURLCreateStringByAddingPercentEscapes, it added CFBridgingRelease

Leave a Reply

Your email address will not be published. Required fields are marked *