Swift'de ilişkili nesneleri ayarlamanın bir yolu var mı?


82

C hedefinden gelince, objc_setAssociatedObject2 nesne arasında bir referansı korumaları için işlev çağırabilirsiniz ; bu, çalışma zamanında bir nesnenin referansı da kaldırılıncaya kadar yok edilmesini istemiyorsanız kullanışlı olabilir. Swift'in buna benzer bir şeyi var mı?


3
objc_setAssociatedObjectSwift'i
Swift'den

Kesip yapıştırmak için TAM KODU OLAN UZUN ÖRNEK: stackoverflow.com/documentation/swift/1085/associated-objects/…
Fattie

Yanıtlar:


132

İşte jckarter'ın cevabından türetilen basit ama eksiksiz bir örnek .

Mevcut bir sınıfa nasıl yeni bir özellik ekleneceğini gösterir. Bunu, bir uzantı bloğunda hesaplanmış bir özellik tanımlayarak yapar. Hesaplanan özellik, ilişkili bir nesne olarak saklanır:

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)
        }
    }
}

DÜZENLE:

Başlatılmamış bir özelliğin değerini almayı desteklemeniz gerekiyorsa ve hatayı almaktan kaçınmak unexpectedly found nil while unwrapping an Optional valueiçin alıcıyı şu şekilde değiştirebilirsiniz:

    get {
        return objc_getAssociatedObject(self, &AssociatedObjectHandle) as? String ?? ""
    }

Bu şekilde tip takma ada uyan bir işlev ayarlayabildiniz mi?
Morkrom

1
@PhamHoan Int bir Swift yerel türüdür. Bunun yerine NSNumber kullanmayı deneyin.
Klaas

1
@PhamHoan Başlatılmamış bir nesne almak için bir çözüm ekledim.
Klaas

1
@Jawwad sanırım, hiç fark etmez. UInt8Bağlantılı orijinal iplik geliyor.
Klaas

1
hey @Jacky - bunun KOPYALA olması gerektiğini söyleyen şey .. lütfen bize bildirin!
Şişko

31

Çözümün bütün destekler değer türlerini de kurduğunuzu değil sadece bu automagicallylar gibi köprülü, vb dize, Int, Double,

Sarmalayıcılar

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
    }
}

Olası bir Sınıf uzantısı (Kullanım örneği)

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)
            }
        }
    }
}

Bu ilişkilendirme bayraklarının ne anlama geldiğini bana açıklar mısınız? Aslında scrollView ile gezinme çubuğunu kaydırmaya çalışıyorum, bu yüzden scrollView'ı uzantı olarak depoluyorum. Hangi bayrakları kullanmalıyım?
göktaşları

1
Ayrıca, bu yöntemi kullanarak [AnyObject] dizisini depolamaya çalışıyorum ama alıcı bana her zaman nil döndürüyor. Her nasılsa, eğer get ise değeri düzgün bir şekilde döndürmüyor.
göktaşları

Kullanmaya karar verdiğiniz ilişkilendirme politikası size bağlıdır, programınıza bağlıdır. Daha fazla bilgi için nshipster.com/associated-objects adresindeki bu makaleye göz atmanızı öneririm . Dizileri saklamaya çalıştım, gayet iyi çalışıyor gibi görünüyor. Bu arada hangi Swift sürümünü kullanıyorsunuz?
HepaKKes

Bu harika. Swift 2.3 için Generics'i Lift'ten alıp kullanmaya son verdim Any. Sanırım
bununla

5
Bu, Swift 3'te çok daha kolaylaştı. Ama yine de, cevabınıza göre burada benim makalem: yar2050.com/2016/11/associated-object-support-for-swift-23.html
Dan Rosenstark

5

Açıkçası, bu yalnızca Objective-C nesneleriyle çalışır. Bununla biraz uğraştıktan sonra, Swift'de aramaları şu şekilde yapabilirsiniz:

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)
}

Bu yöntemi denedim, ancak değer nesnem, swift kodundan başka bir referans olmadığında hemen serbest bırakılıyor gibi görünüyor. Hedef nesnem bir SKNode ve değer nesnem NSObject'i genişleten hızlı bir sınıf. Bu çalışmalı mı?
nacross

Aslında objc_setAssociatedObject sırasında, nesne henüz yaratılmış ve yöntemde daha aşağıda kullanılmış olsa bile deinit çağrılıyor gibi görünüyor.
2014

4

Swift 3.0'da güncelleme Örneğin bu bir 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)
        }
    }
}

3

Swift 5.1 gerektiren modern bir yardımcı yazdım.

/*
 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 yapılarını ücretsiz olarak desteklemesine bile şaşırabilirsiniz.

Swift struct derneği


2

Klaas'ın cevabı sadece Swift 2.1 için:

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

0

#import <objc/runtime.h>Objc_setAssociatedObject'e hızlı kod altında erişmek için brindging başlık dosyanızı eklemeniz yeterli


18
ya da sadece can import ObjectiveCSwift
newacct

0

Yukarıdaki arkadaş sorunuzu cevapladı, ancak kapalı mülklerle ilgiliyse lütfen unutmayın:

''

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
}

''

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.