Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to set associated objects in Swift?

Coming from Objective-C you can call function objc_setAssociatedObject between 2 objects to have them maintain a reference, which can be handy if at runtime you don't want an object to be destroyed until its reference is removed also. Does Swift have anything similar to this?

like image 649
amleszk Avatar asked Jun 10 '14 04:06

amleszk


People also ask

What are associated objects Swift?

Associated Objects is part of Objective-C runtime. It allow you to associate objects at runtime. Simply, you can attach any object to any other object without subclassing.

What is associated object?

Associated Objects—or Associative References, as they were originally known—are a feature of the Objective-C 2.0 runtime, introduced in OS X Snow Leopard (available in iOS 4). The term refers to the following three C functions declared in <objc/runtime.

What is Objc_setassociatedobject?

Sets an associated value for a given object using a given key and association policy.


2 Answers

Here is a simple but complete example derived from jckarter's answer.

It shows how to add a new property to an existing class. It does it by defining a computed property in an extension block. The computed property is stored as an associated object:

import ObjectiveC  // Declare a global var to produce a unique address as the assoc object handle private var AssociatedObjectHandle: UInt8 = 0  extension MyClass {     var stringProperty:String {         get {             return objc_getAssociatedObject(self, &AssociatedObjectHandle) as! String         }         set {             objc_setAssociatedObject(self, &AssociatedObjectHandle, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)         }     } } 

EDIT:

If you need to support getting the value of an uninitialized property and to avoid getting the error unexpectedly found nil while unwrapping an Optional value, you can modify the getter like this:

    get {         return objc_getAssociatedObject(self, &AssociatedObjectHandle) as? String ?? ""     } 
like image 68
Klaas Avatar answered Sep 23 '22 19:09

Klaas


The solution supports all the value types as well, and not only those that are automagically bridged, such as String, Int, Double, etc.

Wrappers

import ObjectiveC  final class Lifted<T> {     let value: T     init(_ x: T) {         value = x     } }  private func lift<T>(x: T) -> Lifted<T>  {     return Lifted(x) }  func setAssociatedObject<T>(object: AnyObject, value: T, associativeKey: UnsafePointer<Void>, policy: objc_AssociationPolicy) {     if let v: AnyObject = value as? AnyObject {         objc_setAssociatedObject(object, associativeKey, v,  policy)     }     else {         objc_setAssociatedObject(object, associativeKey, lift(value),  policy)     } }  func getAssociatedObject<T>(object: AnyObject, associativeKey: UnsafePointer<Void>) -> T? {     if let v = objc_getAssociatedObject(object, associativeKey) as? T {         return v     }     else if let v = objc_getAssociatedObject(object, associativeKey) as? Lifted<T> {         return v.value     }     else {         return nil     } } 

A possible Class extension (Example of usage)

extension UIView {      private struct AssociatedKey {         static var viewExtension = "viewExtension"     }      var referenceTransform: CGAffineTransform? {         get {             return getAssociatedObject(self, associativeKey: &AssociatedKey.viewExtension)         }          set {             if let value = newValue {                 setAssociatedObject(self, value: value, associativeKey: &AssociatedKey.viewExtension, policy: objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)             }         }     } } 
like image 44
HepaKKes Avatar answered Sep 24 '22 19:09

HepaKKes