Есть ли способ установить связанные объекты в Swift?
исходя из objective C вы можете вызвать функцию objc_setAssociatedObject
между 2 объектами, чтобы они поддерживали ссылку, что может быть удобно, если во время выполнения вы не хотите, чтобы объект был уничтожен до тех пор, пока его ссылка не будет удалена. Есть ли у swift что-нибудь подобное?
7 ответов:
вот простой, но полный пример, полученный из ответ jckarter.
Он показывает, как добавить новое свойство к существующему классу. Это делается путем определения вычисляемого свойства в блоке расширения. Вычисленное свойство хранится в виде связанного объекта:
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:
Если вам нужно поддержать получение значения неинициализированного свойства и избежать получения ошибки
unexpectedly found nil while unwrapping an Optional value
, вы можете изменить добытчик вот так:get { return objc_getAssociatedObject(self, &AssociatedObjectHandle) as? String ?? "" }
решение поддерживает все типы значений а также, и не только те, которые автоматически мост, например строка, Int, Double и т. д.
фантики
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 } }
возможный расширение класс (пример использования)
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) } } } }
очевидно, что это работает только с объектами Objective-C. После возиться с этим немного, вот как сделать звонки в 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) }
обновление в Swift 3.0 Например, это 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) } } }
Клаас ответ только для 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
просто добавить
#import <objc/runtime.h>
в вашем заголовочном файле brindging для доступа к objc_setAssociatedObject под swift-кодом
вышеуказанный друг ответил на ваш вопрос, но если это связано с свойствами закрытия, обратите внимание:
"'
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 }
"'