Objective-C から来るとobjc_setAssociatedObject
、2 つのオブジェクト間で関数を呼び出して参照を維持することができます。これは、実行時に参照が削除されるまでオブジェクトを破棄したくない場合に便利です。Swift にはこれに似たものがありますか?
8 に答える
これは、 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
、次のようにゲッターを変更できます。
get {
return objc_getAssociatedObject(self, &AssociatedObjectHandle) as? String ?? ""
}
このソリューションは、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のみのKlaasの回答:
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>
迅速なコードの下で objc_setAssociatedObject にアクセスするには、ブリッジヘッダーファイルを追加するだけです