Viniendo del objetivo C, puede llamar a la función objc_setAssociatedObject
entre 2 objetos para que mantengan una referencia, lo que puede ser útil si en tiempo de ejecución no desea que se destruya un objeto hasta que también se elimine su referencia. ¿Swift tiene algo similar a esto?
82
objc_setAssociatedObject
desde Swift: developer.apple.com/library/prerelease/ios/documentation/Swift/…Respuestas:
Aquí hay un ejemplo simple pero completo derivado de la respuesta de jckarter .
Muestra cómo agregar una nueva propiedad a una clase existente. Lo hace definiendo una propiedad calculada en un bloque de extensión. La propiedad calculada se almacena como un objeto asociado:
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) } } }
EDITAR:
Si necesita admitir la obtención del valor de una propiedad no inicializada y para evitar el error
unexpectedly found nil while unwrapping an Optional value
, puede modificar el getter de la siguiente manera:get { return objc_getAssociatedObject(self, &AssociatedObjectHandle) as? String ?? "" }
fuente
UInt8
proviene del hilo original vinculado.La solución también admite todos los tipos de valores , y no solo aquellos que están puenteados automágicamente , como String, Int, Double, etc.
Envoltorios
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 } }
Una posible extensión de clase (ejemplo de uso)
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) } } } }
fuente
Any
lugar. Creo queObviamente, esto solo funciona con objetos Objective-C. Después de jugar un poco con esto, aquí le mostramos cómo hacer las llamadas en Swift:
import ObjectiveC // Define a variable whose address we'll use as key. // "let" doesn't work here. var kSomeKey = "s" … func someFunc() { objc_setAssociatedObject(target, &kSomeKey, value, UInt(OBJC_ASSOCIATION_RETAIN)) let value : AnyObject! = objc_getAssociatedObject(target, &kSomeKey) }
fuente
Actualización en Swift 3.0 Por ejemplo, este es un UITextField
import Foundation import UIKit import ObjectiveC // Declare a global var to produce a unique address as the assoc object handle var AssociatedObjectHandle: UInt8 = 0 extension UITextField { var nextTextField:UITextField { get { return objc_getAssociatedObject(self, &AssociatedObjectHandle) as! UITextField } set { objc_setAssociatedObject(self, &AssociatedObjectHandle, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } }
fuente
Escribí un ayudante moderno que requiere Swift 5.1.
/* AssociatedObject.swift Copyright © 2020 RFUI. https://github.com/BB9z/iOS-Project-Template The MIT License https://opensource.org/licenses/MIT */ import Foundation /** Objective-C associated value wrapper. Usage ``` private let fooAssociation = AssociatedObject<String>() extension SomeObject { var foo: String? { get { fooAssociation[self] } set { fooAssociation[self] = newValue } } } ``` */ public final class AssociatedObject<T> { private let policy: objc_AssociationPolicy /// Creates an associated value wrapper. /// - Parameter policy: The policy for the association. public init(policy: objc_AssociationPolicy = .OBJC_ASSOCIATION_RETAIN_NONATOMIC) { self.policy = policy } /// Accesses the associated value. /// - Parameter index: The source object for the association. public subscript(index: AnyObject) -> T? { get { objc_getAssociatedObject(index, Unmanaged.passUnretained(self).toOpaque()) as? T } set { objc_setAssociatedObject(index, Unmanaged.passUnretained(self).toOpaque(), newValue, policy) } } }
Puede que te sorprenda que incluso admita estructuras Swift de forma gratuita.
fuente
Klaas responde solo para Swift 2.1:
import ObjectiveC let value = NSUUID().UUIDString var associationKey: UInt8 = 0 objc_setAssociatedObject(parentObject, &associationKey, value, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) let fetchedValue = objc_getAssociatedObject(parentObject, &associationKey) as! String
fuente
Simplemente agregue
#import <objc/runtime.h>
su archivo de encabezado de puente para acceder a objc_setAssociatedObject bajo código rápidofuente
import ObjectiveC
en SwiftEl amigo anterior respondió a su pregunta, pero si está relacionada con las propiedades de cierre, tenga en cuenta:
''
import UIKit public extension UICollectionView { typealias XYRearrangeNewDataBlock = (_ newData: [Any]) -> Void typealias XYRearrangeOriginaDataBlock = () -> [Any] // MARK:- associat key private struct xy_associatedKeys { static var originalDataBlockKey = "xy_originalDataBlockKey" static var newDataBlockKey = "xy_newDataBlockKey" } private class BlockContainer { var rearrangeNewDataBlock: XYRearrangeNewDataBlock? var rearrangeOriginaDataBlock: XYRearrangeOriginaDataBlock? } private var newDataBlock: BlockContainer? { get { if let newDataBlock = objc_getAssociatedObject(self, &xy_associatedKeys.newDataBlockKey) as? BlockContainer { return newDataBlock } return nil } set(newValue) { objc_setAssociatedObject(self, xy_associatedKeys.newDataBlockKey, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC) } } convenience init(collectionVewFlowLayout : UICollectionViewFlowLayout, originalDataBlock: @escaping XYRearrangeOriginaDataBlock, newDataBlock: @escaping XYRearrangeNewDataBlock) { self.init() let blockContainer: BlockContainer = BlockContainer() blockContainer.rearrangeNewDataBlock = newDataBlock blockContainer.rearrangeOriginaDataBlock = originalDataBlock self.newDataBlock = blockContainer }
''
fuente