Wychodząc z celu C możesz wywołać funkcję objc_setAssociatedObject
między 2 obiektami, aby utrzymywały odniesienie, co może być przydatne, jeśli w czasie wykonywania nie chcesz, aby obiekt został zniszczony, dopóki jego odwołanie nie zostanie również usunięte. Czy Swift ma coś podobnego do tego?
82
objc_setAssociatedObject
ze Swift: developer.apple.com/library/prerelease/ios/documentation/Swift/…Odpowiedzi:
Oto prosty, ale kompletny przykład zaczerpnięty z odpowiedzi jckartera .
Pokazuje, jak dodać nową właściwość do istniejącej klasy. Robi to poprzez zdefiniowanie obliczonej właściwości w bloku rozszerzenia. Obliczona właściwość jest przechowywana jako skojarzony obiekt:
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) } } }
EDYTOWAĆ:
Jeśli potrzebujesz obsługiwać pobieranie wartości niezainicjowanej właściwości i unikać otrzymywania błędu
unexpectedly found nil while unwrapping an Optional value
, możesz zmodyfikować getter w następujący sposób:get { return objc_getAssociatedObject(self, &AssociatedObjectHandle) as? String ?? "" }
źródło
UInt8
Pochodzi z połączonej pierwotnej nici.Rozwiązanie obsługuje również wszystkie typy wartości , a nie tylko te, które są mostkowane automagicznie , takie jak String, Int, Double itp.
Owijki
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 } }
Możliwe rozszerzenie klasy (przykład użycia)
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) } } } }
źródło
Any
zamiast tego. Myślę, że opublikuję artykuł na ten tematOczywiście działa to tylko z obiektami Objective-C. Po trochę zabawie przy tym, oto jak wykonywać połączenia w 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) }
źródło
Aktualizacja w Swift 3.0 Na przykład jest to 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) } } }
źródło
Napisałem nowoczesnego pomocnika, który wymaga 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) } } }
Możesz być zaskoczony, że obsługuje on nawet struktury Swift za darmo.
źródło
Klaas odpowiada tylko dla 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
źródło
Po prostu dodaj
#import <objc/runtime.h>
swój plik nagłówkowy brindging, aby uzyskać dostęp do objc_setAssociatedObject w szybkim kodzieźródło
import ObjectiveC
w SwiftPowyższy znajomy odpowiedział na Twoje pytanie, ale jeśli dotyczy ono właściwości zamknięcia, pamiętaj:
`` ''
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 }
`` ''
źródło