Skip to content

Instantly share code, notes, and snippets.

@nicklockwood
Last active November 20, 2018 10:02
  • Star 97 You must be signed in to star a gist
  • Fork 31 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save nicklockwood/1563325 to your computer and use it in GitHub Desktop.
ARC Helper
//
// ARC Helper
//
// Version 2.2
//
// Created by Nick Lockwood on 05/01/2012.
// Copyright 2012 Charcoal Design
//
// Distributed under the permissive zlib license
// Get the latest version from here:
//
// https://gist.github.com/1563325
//
#import <Availability.h>
#undef ah_retain
#undef ah_dealloc
#undef ah_autorelease autorelease
#undef ah_dealloc dealloc
#if __has_feature(objc_arc)
#define ah_retain self
#define ah_release self
#define ah_autorelease self
#define ah_dealloc self
#else
#define ah_retain retain
#define ah_release release
#define ah_autorelease autorelease
#define ah_dealloc dealloc
#undef __bridge
#define __bridge
#undef __bridge_transfer
#define __bridge_transfer
#endif
// Weak reference support
#import <Availability.h>
#if !__has_feature(objc_arc_weak)
#undef ah_weak
#define ah_weak unsafe_unretained
#undef __ah_weak
#define __ah_weak __unsafe_unretained
#endif
// Weak delegate support
#import <Availability.h>
#undef ah_weak_delegate
#undef __ah_weak_delegate
#if __has_feature(objc_arc_weak) && \
(!(defined __MAC_OS_X_VERSION_MIN_REQUIRED) || \
__MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_8)
#define ah_weak_delegate weak
#define __ah_weak_delegate __weak
#else
#define ah_weak_delegate unsafe_unretained
#define __ah_weak_delegate __unsafe_unretained
#endif
// ARC Helper ends
@nicklockwood
Copy link
Author

Purpose

ARC Helper is an Objective-C snippet that can be either pasted into the header of a file or used as a header file in its own right. It is designed to solve the problem of writing library code that can be used with both ARC and non-ARC projects without needing to be modified. It is designed to work with both Mac OS and iOS projects.

ARC Helper is modular. For projects that are designed to work only with ARC enabled, you can use just the second and/or 3rd modules on their own (the part from "Weak reference support" down) and omit the first block. For projects that are iOS only, you don't need the 3rd module (the part from "Weak delegate support" down), which deals with weak references to classes such as NSWindowController on Mac OS that do not support weak references under Mac OS 10.7.

Supported OS & SDK Versions

  • Supported build target - iOS 5.1 / Mac OS 10.7 (Xcode 4.3.3, Apple LLVM compiler 3.1)
  • Earliest supported deployment target - iOS 4.3 / Mac OS 10.6
  • Earliest compatible deployment target - iOS 3.0 / Mac OS 10.6

NOTE: 'Supported' means that the library has been tested with this version. 'Compatible' means that the library should work on this iOS version (i.e. it doesn't rely on any unavailable SDK features) but is no longer being tested for compatibility and may require tweaking or bug fixes to run correctly.

Installation

For a single-class library, the easiest way to use ARC Helper is to paste the code into the top of your .h file.

For a multi-class library, you may prefer to save the ARC Helper snippet as a header file (suggested name is ARCHelper.h) and import it into any classes that use it.

Note that the ARC Helper code can safely be included multiple times in a project, so you need not worry about duplicating it in multiple files, although issues may arise if your project includes multiple incompatible versions of the ARC Helper code.

Usage

ARC Helper consists of a set of macros that are designed to be used in place of the standard Objective-C memory management functions and macros. These macros will generate code appropriate to the build target.

On the rare occasions (such as a dealloc block) where you wish to eliminate several lines or a whole method or under ARC, you can use the __has_feature(objc_arc) macro to write branching logic based on whether ARC is available or not. For the most part though, you can keep your code cleaner by generating the correct code on a statement-by-statement basis with the ARC Helper macros:

ah_release
ah_autorelease

When running under ARC, ARCHelper replaces these methods with macros that do nothing. You can therefore use [object ah_release] and [object ah_autorelease] exactly as you would in a non-ARX project and they not trigger a compiler warning when compiled under ARC.

ah_retain

Use this instead of retain to retain an object. Use it like this: [object ah_retain]. In a non-ARC project this maps to retain and in an ARC project it does nothing.

ah_dealloc

Use this instead of dealloc in a [super dealloc] statement, i.e. [super ah_dealloc]. Note that if your dealloc statement only contains release statements, you can omit it completely under ARC by wrapping it in #if !__has_feature(objc_arc) ... #endif.

__bridge
__bridge_transfer

This is used when casting between C-style pointers and NSObject references, e.g. (__bridge void *)self. __bridge isn't normally defined for non-ARC targets, so ARCHelper defines it and maps it to nothing. Note that for converting between toll-free-bridged Core Foundation types and their Cocoa equivalents you are better off using the CFBridgingRetain() and CFBridgingRelease() functions instead.

ah_weak

The ah_weak reference type should be used for defining weak/unretained object properties, e.g. @property (nonatomic, weak) id foo;, write @property (nonatomic, ah_weak) id foo;. The weak property type is unsupported on non-ARC targets, or on ARC targets with a deployment target less than iOS 5 or Mac OS 7. ARCHelper defines ah_weak as unsafe_unretained in these cases, so it can safely be used regardless of the target platform.

__ah_weak

Similar to to the ah_weak property specifier, but used for ivars. The same rules apply - ARCHelper safely maps this to __unsafe_unretained or nothing, depending on the target platform.

ah_weak_delegate
__ah_weak_delegate

Annoyingly, on Mac OS 10.7 a number of classes including NSWindowController and NSViewController do not support weak references. When creating a user interface component library that uses the delegate pattern to communicate with a controller, you would naturally want to use a weak reference for the delegate, but it's not safe to use weak references for this on Mac OS, even when targeting 10.7 and above. The ah_weak_delegate property macro (and associated __ah_weak_delegate ivar macro) can be used safely for this purpose. On iOS they work just the same as weak, but on Mac OS they won't create a weak reference unless your deployment target is 10.8 or above.

@nicklockwood
Copy link
Author

Release Notes

Version 2.2

  • Name-spaced release and autorelease to prevent macros "leaking" into non-arc code and causing problems
  • Renamed ah_weak to weak_delegate
  • Added #define __bridge_transfer
  • Version 2.2 is only partially compatible with previous versions - be careful when mixing and matching

Version 2.1

  • Added weak delegate support for weak references to NSWindowController, etc on Mac OS
  • 2.1 is fully compatible with version 2.0, you won't get errors by mixing classes that use both versions in the same project

Version 2.0

  • Completely rewritten to be shorter and more convenient. Now uses standard attribute and method names where possible
  • 2.0 is fully compatible with version 1.3.x, you won't get errors by mixing classes that use both versions in the same project

Version 1.3.1

  • Added <Availability.h> import to weak reference module in case it isn't included in the prefix.pch file
  • 1.3.1 is fully compatible with version 1.3, you won't get errors by mixing classes that use both versions in the same project

Version 1.3

  • Added __AH_BRIDGE macro
  • It is advised that if you are using the __AH_BRIDGE macro in your code, you upgrade all instances of ARC Helper in your project to 1.3, or you may have compatibility issues

Version 1.2.2

  • Added void definitions for AH_RELEASE and AH_SUPER_DEALLOC to avoid masking compiler errors and to ensure that AH_RELEASE works as expected if passed the result of a method call
  • 1.2.1 is fully compatible with 1.2 and 1.2.1, you won't get errors by mixing classes that use both versions in the same project

Version 1.2.1

  • Added additional parentheses to avoid masking compiler errors
  • 1.2.1 is fully compatible with 1.2, you won't get errors by mixing classes that use both versions in the same project

Version 1.2

  • Simplified library by dropping support for the LLVM GCC 4.2 compiler

Version 1.1

  • Refactored for compatibility with ARC Helper Lite

Version 1.0

  • Initial release.

@nicklockwood
Copy link
Author

License

ARC Helper

Version 2.1, June 24th, 2012

Copyright (C) 2012 Charcoal Design

This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:

  1. The origin of this software must not be misrepresented; you must not
    claim that you wrote the original software. If you use this software
    in a product, an acknowledgment in the product documentation would be
    appreciated but is not required.
  2. Altered source versions must be plainly marked as such, and must not be
    misrepresented as being the original software.
  3. This notice may not be removed or altered from any source distribution.

@peyton
Copy link

peyton commented Feb 21, 2012

Hey great gist, but as-written it eats compiler errors. I forgot a bracket within an AH_AUTORELEASE call and it took forever to hunt down. Parameters just need to be wrapped in parentheses like so:

#define AH_AUTORELEASE(x) [(x) autorelease]

Fix here: https://gist.github.com/1878186.

@nicklockwood
Copy link
Author

Thanks! I like your solution, and it shouldn't cause any problems with version compatibility. I've updated the gist with your fix.

@anystone
Copy link

Great idea Nick - thank you.

One thing that I also found I needed was some mechanism to specify the property type for IBOutlets. In ARC flavour, they should be declared as weak properties, while in non-ARC they are retained.

I added a new define:

In ARC:

define AH_IB_OUTLET AH_WEAK

Non-ARC:

define AH_IB_OUTLET AH_STRONG

@nicklockwood
Copy link
Author

I don't think there's a problem with using strong outlets in ARC code, as long as you nil them out in your viewDidUnload methods as before.

@anystone
Copy link

Fair enough - it is what Apple recommends, but you're probably right in that it will work either way.

I've got some code that is incoming by a developer who went straight to ARC and as such I'm not that that he's actually done the nil'ing out in viewDidUnload.

Thanks again Nick - it's a handy little wrapper.

@nicklockwood
Copy link
Author

If you need code to work in a non-ARC environment (or on iOS4, which doesn't support true weak references, only unsafe unretained references), you'll need to add the nils in viewDidUnload anyway, so you may as well use strong outlets. If you don't need it to work in a non-ARC environment, you don't need to use this gist anyway.

As for forgetting to nil strong outlets in your viewDidUnload, it's not the end of the world. It won't crash and it won't leak, it just means that offscreen views use up a bit more memory because they don't free their subviews. On the other hand, if you forget to nil out an unsafe unretained outlet and you try to access it, you'll crash, which is a good argument for using strong outlets (bigger memory footprint is preferable to random crashes).

So in conclusion, if you are supporting iOS4 and up, stick with strong outlets, and if you are supporting iOS5 and up, use ARC and weak outlets, but don't bother using this gist.

@HIRANO-Satoshi
Copy link

Hi,

We wrote another gist for ARC support. Our ARCMacro.h is very simple and natural to use.

https://gist.github.com/2823399

@nicklockwood
Copy link
Author

Nice. I've updated ARCHelper to use a similar approach.

@Coeur
Copy link

Coeur commented Sep 20, 2012

Nice. You can add this too:
#define __bridge_transfer

@Coeur
Copy link

Coeur commented Nov 2, 2012

on my fork, I have changed "release" to "ah_release" in order to be compatible with structs CFDictionaryKeyCallBacks and CFDictionaryValueCallBack from CoreFoundation file CFDictionary.h

@nverinaud
Copy link

IMO it is a bad gist, it breaks Convert to Objective-C ARC... tool by hiding release, autorelease and retain calls behind self.

Moreover it reduces performance because you will call self under ARC instead of simply removing calls. Then ARC will add his own release calls instead of using yours.

I suggest wrap your non-ARC code around preprocessor directives like in this file: https://github.com/nverinaud/NVUIGradientButton/blob/master/lib/NVUIGradientButton/NVUIGradientButton.m.

@nicklockwood
Copy link
Author

@nverinaud you're right, but I changed it to use the ah prefix for everything some time ago (before your comment), so that criticism is no longer applicable.

The performance thing is basically a non-issue.

@nicklockwood
Copy link
Author

@nverinaud you may prefer this older version if you are worried about performance:

https://gist.github.com/nicklockwood/1563325/4d32686a6058cb9bc409767ee7cc37a7993cb2df

@hungntv
Copy link

hungntv commented Jul 15, 2013

Xcode gave me "Extra tokens at end of #undef directive" warning message.

#undef ah_autorelease autorelease
#undef ah_dealloc dealloc

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment