86

Objective-C から来るとobjc_setAssociatedObject、2 つのオブジェクト間で関数を呼び出して参照を維持することができます。これは、実行時に参照が削除されるまでオブジェクトを破棄したくない場合に便利です。Swift にはこれに似たものがありますか?

4

8 に答える 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、次のようにゲッターを変更できます。

    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 に答える
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

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
于 2016-02-04T08:43:03.067 に答える
0

#import <objc/runtime.h>迅速なコードの下で objc_setAssociatedObject にアクセスするには、ブリッジヘッダーファイルを追加するだけです

于 2014-06-10T05:38:15.637 に答える