来自 Objective-C,您可以objc_setAssociatedObject
在 2 个对象之间调用函数以让它们维护一个引用,如果在运行时您不希望一个对象被销毁,直到它的引用也被删除,这会很方便。斯威夫特有类似的东西吗?
问问题
37933 次
8 回答
141
这是一个来自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)
}
}
}
编辑:
如果您需要支持获取未初始化属性的值并避免出现错误unexpectedly found nil while unwrapping an Optional value
,您可以像这样修改 getter:
get {
return objc_getAssociatedObject(self, &AssociatedObjectHandle) as? String ?? ""
}
于 2014-08-21T14:03:27.497 回答
31
该解决方案还支持所有值类型,而不仅仅是那些自动桥接的值类型,例如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)
}
}
}
}
于 2015-04-15T23:13:09.843 回答
8
我写了一个现代包装器,可在https://github.com/b9swift/AssociatedObject
你可能会惊讶它甚至免费支持 Swift 结构。
于 2020-09-04T09:25:02.553 回答
5
显然,这只适用于 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)
}
于 2014-07-05T10:32:12.960 回答
4
在 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)
}
}
}
于 2016-11-29T09:28:20.613 回答
2
Klaas 只为 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
于 2016-02-04T08:43:03.067 回答
0
只需添加#import <objc/runtime.h>
您的 brinding 头文件即可在 swift 代码下访问 objc_setAssociatedObject
于 2014-06-10T05:38:15.637 回答
0
上面的朋友已经回答了你的问题,但是如果和闭包属性有关,请注意:
```
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
}
```
于 2016-11-10T08:22:33.603 回答