Providing objc runtime associated object keys from Swift

Hi, I'm looking for some help clarifying a few questions about proper use of the objective-c associated object functions when called from Swift. In particular I'm wondering what types of values meet the expected requirements for the association keys used in the objc_get/setAssociatedObject functions. The API signature (in Swift) says that a key must be an UnsafeRawPointer and I’ve seen various patterns for achieving this in practice.

When calling these functions from objective-c, I historically used the following patterns:

// static value holding its own address
static const void *const someKey = &someKey;

// the _cmd value or selector of an objc method
objc_setAssociatedObject(self, _cmd, ...);

I've always assumed these approaches were somewhat preferable to others (like allocating memory for an object to be used as the key, or introducing a new string literal) because they result in minimal resource usage for key creation.

In Swift I've seen the following techiques used for providing the association keys:

// global variables of various types
private var globalStringKey = "global_string_key"
private var globalOptionalKey: Void? = nil
private var globalVoidKey: Void = ()

// static variables of various types
extension SomeType {
    private static var globalExtensionKey = "global_extension_key"
}

// small heap allocations stored in globals
private var globalHeapAllocationKey = malloc(1)

And then usage with the association functions looks like:

objc_getAssociatedObject(object, &someKey)

I'm curious which of the approaches for defining the keys in Swift satisfy the semantic requirements of the runtime association functions, and which have the least resource overhead. Are there any particular patterns to avoid, or to prefer over others? If there are any other approaches I've missed, I'd also be interested to learn what they are. Thanks!

Hi Jaimie,

Converting an inout String to a pointer is quite dangerous in general because you don't always get a pointer to the characters. Swift 5.9 will warn you, and that warning may become an error down the line. You can always explicitly force the conversion if you like using withUnsafePointer. As an associated object key, it doesn't matter what the address points to. But it's a technique that breaks badly in other contexts.

Here's a discussion about associated object String keys with some suggested alternatives:

Regarding the parent object type, we want to eventually ban associated objects on native Swift classes (otherwise there's no way to reason about the side effects of releasing an ARC reference). I suggest declaring the parent using @objc if it's defined in Swift.

3 Likes