来自目标C,您可以在两个对象之间调用函数objc_setAssociatedObject
以使它们维护一个引用,如果在运行时您不希望销毁对象直到其引用也被删除,这可能很方便。斯威夫特有类似的东西吗?
答案 0 :(得分:116)
以下是源自jckarter's answer的简单但完整的示例。
它显示了如何向现有类添加新属性。它通过在扩展块中定义计算属性来实现。 computed属性存储为关联对象:
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 ?? ""
}
答案 1 :(得分:27)
该解决方案也支持所有值类型,而不仅仅是自动桥接的那些,例如 String,Int,Double等< / em>的
<强>包装器强>
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)
}
}
}
}
答案 2 :(得分: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)
}
答案 3 :(得分:3)
在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)
}
}
}
答案 4 :(得分:3)
我写了一个需要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) }
}
}
您可能会惊讶于它甚至免费支持Swift结构。
答案 5 :(得分: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
答案 6 :(得分:0)
只需在你的brindging头文件中添加#import <objc/runtime.h>
即可在swift代码下访问objc_setAssociatedObject
答案 7 :(得分: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
}
```