NSObject+AssociatedValues.swift 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. import Foundation
  2. struct AssociationKey<Value> {
  3. fileprivate let address: UnsafeRawPointer
  4. fileprivate let `default`: Value!
  5. /// Create an ObjC association key from a `StaticString`.
  6. ///
  7. /// - precondition: `key` has a pointer representation.
  8. ///
  9. /// - parameters:
  10. /// - default: The default value, or `nil` to trap on undefined value. It is
  11. /// ignored if `Value` is an optional.
  12. init(_ key: StaticString, default: Value? = nil) {
  13. assert(key.hasPointerRepresentation, "AssociationKey.init key has no hasPointerRepresentation")
  14. address = UnsafeRawPointer(key.utf8Start)
  15. self.default = `default`
  16. }
  17. }
  18. struct Associations<Base: AnyObject> {
  19. private let base: Base
  20. init(_ base: Base) {
  21. self.base = base
  22. }
  23. }
  24. extension NSObjectProtocol {
  25. @nonobjc var associations: Associations<Self> {
  26. Associations(self)
  27. }
  28. func getAssociatedValue<T>(forKey key: StaticString = #function) -> T? {
  29. associations.value(forKey: AssociationKey<T?>(key))
  30. }
  31. func setAssociatedValue<T>(forKey key: StaticString = #function, value: T?) {
  32. associations.setValue(value, forKey: AssociationKey<T?>(key))
  33. }
  34. }
  35. extension Associations {
  36. /// Retrieve the associated value for the specified key.
  37. ///
  38. /// - parameters:
  39. /// - key: The key.
  40. ///
  41. /// - returns: The associated value, or the default value if no value has been
  42. /// associated with the key.
  43. func value<Value>(forKey key: AssociationKey<Value>) -> Value {
  44. (objc_getAssociatedObject(base, key.address) as! Value?) ?? key.default
  45. }
  46. /// Retrieve the associated value for the specified key.
  47. ///
  48. /// - parameters:
  49. /// - key: The key.
  50. ///
  51. /// - returns: The associated value, or `nil` if no value is associated with
  52. /// the key.
  53. func value<Value>(forKey key: AssociationKey<Value?>) -> Value? {
  54. objc_getAssociatedObject(base, key.address) as! Value?
  55. }
  56. /// Set the associated value for the specified key.
  57. ///
  58. /// - parameters:
  59. /// - value: The value to be associated.
  60. /// - key: The key.
  61. func setValue<Value>(_ value: Value, forKey key: AssociationKey<Value>) {
  62. objc_setAssociatedObject(base, key.address, value, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
  63. }
  64. /// Set the associated value for the specified key.
  65. ///
  66. /// - parameters:
  67. /// - value: The value to be associated.
  68. /// - key: The key.
  69. func setValue<Value>(_ value: Value?, forKey key: AssociationKey<Value?>) {
  70. objc_setAssociatedObject(base, key.address, value, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
  71. }
  72. }