स्विफ्ट में कैसे संपत्तियों को संग्रहीत किया जाए, उसी तरह जो मैंने ऑब्जेक्टिव-सी पर किया था?


132

मैं ऑब्जेक्टिव-सी से स्विफ्ट में एक एप्लिकेशन को स्विच कर रहा हूं, जिसमें मेरे पास संग्रहीत संपत्तियों के साथ कुछ श्रेणियां हैं, उदाहरण के लिए:

@interface UIView (MyCategory)

- (void)alignToView:(UIView *)view
          alignment:(UIViewRelativeAlignment)alignment;
- (UIView *)clone;

@property (strong) PFObject *xo;
@property (nonatomic) BOOL isAnimating;

@end

चूंकि स्विफ्ट एक्सटेंशन इन जैसे संग्रहीत गुणों को स्वीकार नहीं करते हैं, मुझे नहीं पता कि ओब्जेक्ट कोड के समान संरचना को कैसे बनाए रखा जाए। मेरे ऐप के लिए संग्रहीत गुण वास्तव में महत्वपूर्ण हैं और मेरा मानना ​​है कि ऐप्पल ने स्विफ्ट में इसे करने के लिए कुछ समाधान बनाया होगा।

जैसा कि जौ ने कहा, जो मैं देख रहा था वह वास्तव में संबद्ध वस्तुओं का उपयोग कर रहा था, इसलिए मैंने (एक और संदर्भ में) किया:

import Foundation
import QuartzCore
import ObjectiveC

extension CALayer {
    var shapeLayer: CAShapeLayer? {
        get {
            return objc_getAssociatedObject(self, "shapeLayer") as? CAShapeLayer
        }
        set(newValue) {
            objc_setAssociatedObject(self, "shapeLayer", newValue, UInt(OBJC_ASSOCIATION_RETAIN))
        }
    }

    var initialPath: CGPathRef! {
        get {
            return objc_getAssociatedObject(self, "initialPath") as CGPathRef
        }
        set {
            objc_setAssociatedObject(self, "initialPath", newValue, UInt(OBJC_ASSOCIATION_RETAIN))
        }
    }
}

लेकिन मुझे ऐसा करते समय एक EXC_BAD_ACCESS मिलता है:

class UIBubble : UIView {
    required init(coder aDecoder: NSCoder) {
        ...
        self.layer.shapeLayer = CAShapeLayer()
        ...
    }
}

कोई विचार?


11
ऑब्जेक्टिव-सी श्रेणी श्रेणियां या तो उदाहरण चर को परिभाषित नहीं कर सकती हैं, तो आपको उन गुणों का एहसास कैसे हुआ?
मार्टिन आर

निश्चित नहीं है कि आपको खराब एक्सेस क्यों मिलता है, लेकिन आपका कोड काम नहीं करना चाहिए। आप अलग-अलग मान सेट कर रहे हैं setAssociateObject और getAssociatedObject। कुंजी के रूप में स्ट्रिंग "शेप लेयर" का उपयोग करना गलत है, यह पॉइंटर है (यह वास्तव में पता है) यह कुंजी है, न कि वह जो इंगित करता है। अलग-अलग पते पर रहने वाले दो समान तार दो अलग-अलग कुंजी हैं। जौ के उत्तर की समीक्षा करें और ध्यान दें कि उन्होंने xoAssociationKey को वैश्विक चर के रूप में कैसे परिभाषित किया है, इसलिए सेटिंग / प्राप्त करते समय यह एक ही कुंजी / सूचक है।
सेफफैस्टएक्सप्रेसिव

जवाबों:


50

एसोसिएटेड ऑब्जेक्ट्स API उपयोग करने के लिए थोड़ा बोझिल है। आप एक सहायक वर्ग के साथ अधिकांश बॉयलरप्लेट निकाल सकते हैं।

public final class ObjectAssociation<T: AnyObject> {

    private let policy: objc_AssociationPolicy

    /// - Parameter policy: An association policy that will be used when linking objects.
    public init(policy: objc_AssociationPolicy = .OBJC_ASSOCIATION_RETAIN_NONATOMIC) {

        self.policy = policy
    }

    /// Accesses associated object.
    /// - Parameter index: An object whose associated object is to be accessed.
    public subscript(index: AnyObject) -> T? {

        get { return objc_getAssociatedObject(index, Unmanaged.passUnretained(self).toOpaque()) as! T? }
        set { objc_setAssociatedObject(index, Unmanaged.passUnretained(self).toOpaque(), newValue, policy) }
    }
}

बशर्ते कि आप उद्देश्य-सी वर्ग के लिए एक संपत्ति को अधिक पठनीय तरीके से "जोड़" सकते हैं:

extension SomeType {

    private static let association = ObjectAssociation<NSObject>()

    var simulatedProperty: NSObject? {

        get { return SomeType.association[self] }
        set { SomeType.association[self] = newValue }
    }
}

समाधान के लिए:

extension CALayer {

    private static let initialPathAssociation = ObjectAssociation<CGPath>()
    private static let shapeLayerAssociation = ObjectAssociation<CAShapeLayer>()

    var initialPath: CGPath! {
        get { return CALayer.initialPathAssociation[self] }
        set { CALayer.initialPathAssociation[self] = newValue }
    }

    var shapeLayer: CAShapeLayer? {
        get { return CALayer.shapeLayerAssociation[self] }
        set { CALayer.shapeLayerAssociation[self] = newValue }
    }
}

ठीक है अगर मुझे इंट, बूल और आदि को स्टोर करना है तो क्या करें?
व्याचेस्लाव गेरिकोव

1
ऑब्जेक्ट एसोसिएशन के माध्यम से स्विफ्ट प्रकारों को सीधे स्टोर करना संभव नहीं है। आप एग स्टोर कर सकते हैं NSNumberया NSValueअतिरिक्त जोड़ीदार लिख सकते हैं जो आपके इच्छित प्रकार (इंट, बूल आदि) के होंगे।
वोज्शिएक नागरोडज़की

1
चेतावनी यह स्ट्रक्चर्स के लिए काम नहीं करता है, क्योंकि ऑब्जेक्टिव-सी रनटाइम लाइब्रेरी केवल उन वर्गों को सपोर्ट करती है, जिनके अनुरूपNSObjectProtocol
चार्लटन प्रोवेट्स

2
@CharltonProvatas इस एपीआई के साथ संरचनाओं का उपयोग करना संभव नहीं है, वे AnyObject प्रोटोकॉल के अनुरूप नहीं हैं।
वोज्शिएक नागरोडज़्की

@VyachaslavGerchicov [String]?एक संरचना है, यह इंट, बूल के लिए एक ही मामला है। आप उदाहरणों NSArrayका एक संग्रह रखने के लिए उपयोग कर सकते हैं NSString
वोज्शिएक नागरोडज़की

184

जैसा कि उद्देश्य-सी में, आप मौजूदा कक्षाओं में संग्रहीत संपत्ति नहीं जोड़ सकते हैं। यदि आप एक उद्देश्य-सी वर्ग ( UIViewनिश्चित रूप से एक है) का विस्तार कर रहे हैं , तो आप अभी भी संग्रहीत गुणों का अनुकरण करने के लिए संबद्ध वस्तुओं का उपयोग कर सकते हैं:

स्विफ्ट के लिए 1

import ObjectiveC

private var xoAssociationKey: UInt8 = 0

extension UIView {
    var xo: PFObject! {
        get {
            return objc_getAssociatedObject(self, &xoAssociationKey) as? PFObject
        }
        set(newValue) {
            objc_setAssociatedObject(self, &xoAssociationKey, newValue, objc_AssociationPolicy(OBJC_ASSOCIATION_RETAIN))
        }
    }
}

संघ की कुंजी एक सूचक है जो प्रत्येक संघ के लिए अद्वितीय होना चाहिए। उसके लिए, हम एक निजी वैश्विक चर बनाते हैं और इसे &ऑपरेटर के साथ कुंजी के रूप में मेमोरी एड्रेस का उपयोग करते हैं । देखें कोको और ऑब्जेक्टिव-सी के साथ का उपयोग करते हुए स्विफ्ट और अधिक जानकारी के लिए कैसे संकेत स्विफ्ट में संभाला जाता है।

स्विफ्ट 2 और 3 के लिए अद्यतन

import ObjectiveC

private var xoAssociationKey: UInt8 = 0

extension UIView {
    var xo: PFObject! {
        get {
            return objc_getAssociatedObject(self, &xoAssociationKey) as? PFObject
        }
        set(newValue) {
            objc_setAssociatedObject(self, &xoAssociationKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
        }
    }
}

स्विफ्ट 4 के लिए अद्यतन

स्विफ्ट 4 में, यह बहुत अधिक सरल है। धारक संरचना में निजी मूल्य शामिल होगा जो हमारी गणना की गई संपत्ति दुनिया को उजागर करेगी, बजाय संग्रहीत संपत्ति व्यवहार का भ्रम देगा।

स्रोत

extension UIViewController {
    struct Holder {
        static var _myComputedProperty:Bool = false
    }
    var myComputedProperty:Bool {
        get {
            return Holder._myComputedProperty
        }
        set(newValue) {
            Holder._myComputedProperty = newValue
        }
    }
}

2
@ यार के बारे में पता नहीं था objc_AssociationPolicy, धन्यवाद! मैंने जवाब अपडेट किया है
jou

2
Swift2 में आपको objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN का उपयोग करना होगा
Tom

1
@SimplGy कृपया अपने कोड को किसी अन्य के उत्तर में अपने कोड को संपादित करने के बजाय एक अतिरिक्त उत्तर के रूप में जोड़ें।
जाल

11
यदि आप एक्सटेंशन से संपत्ति का उपयोग करने वाले UIViewController के 1 से अधिक उदाहरण चाहते हैं, तो Swift4 समाधान काम नहीं करता है। कृपया स्रोत में अपडेट देखें।
--ुर

9
स्विफ्ट 4 का उदाहरण कई उदाहरणों के साथ काम नहीं करता है, और शायद यहां नहीं होना चाहिए।
कोलिन कॉर्बिन

36

इसलिए मुझे लगता है कि मुझे एक विधि मिली जो ऊपर वाले की तुलना में क्लीनर का काम करती है क्योंकि इसके लिए किसी भी वैश्विक चर की आवश्यकता नहीं होती है। मुझे यह यहाँ से मिला: http://nshipster.com/swift-objc-runtime/

सार यह है कि आप एक संरचना का उपयोग करते हैं:

extension UIViewController {
    private struct AssociatedKeys {
        static var DescriptiveName = "nsh_DescriptiveName"
    }

    var descriptiveName: String? {
        get {
            return objc_getAssociatedObject(self, &AssociatedKeys.DescriptiveName) as? String
        }
        set {
            if let newValue = newValue {
                objc_setAssociatedObject(
                    self,
                    &AssociatedKeys.DescriptiveName,
                    newValue as NSString?,
                    UInt(OBJC_ASSOCIATION_RETAIN_NONATOMIC)
                )
            }
        }
    }
}

स्विफ्ट 2 के लिए अद्यतन

private struct AssociatedKeys {
    static var displayed = "displayed"
}

//this lets us check to see if the item is supposed to be displayed or not
var displayed : Bool {
    get {
        guard let number = objc_getAssociatedObject(self, &AssociatedKeys.displayed) as? NSNumber else {
            return true
        }
        return number.boolValue
    }

    set(value) {
        objc_setAssociatedObject(self,&AssociatedKeys.displayed,NSNumber(bool: value),objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    }
}

@AKWlS मैंने आपकी तरह कोड लागू किया है, लेकिन तेजी से 2.0 में अपडेट होने पर, मुझे त्रुटि मिल रही है कि टाइप 'objc_AssociationPolicy)' की तर्क सूची के साथ 'UInt' टाइप के लिए इनिशियलाइज़र को आमंत्रित नहीं कर सकता। अगली टिप्पणी में कोड
user2363025

स्विफ्ट 2.0 के लिए अपडेट कैसे करें? var पोस्टडेसक्रिप्शन: स्ट्रिंग? {get {वापसी objc_getAssociatedObject (self, & AssociatedKeys.postDescription) के रूप में? स्ट्रिंग} सेट {यदि जाने newValue = newValue {objc_setAssociatedObject (स्वयं, और AssociatedKeys.postDescription, NSString ?, uint के रूप में newValue (objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC))}}}
user2363025

1
काम नहीं करता है: स्थिर संस्करण का अर्थ है कि संपत्ति का मूल्य सभी उदाहरणों के लिए समान है
फ्राई

@fry - यह मेरे लिए काम करना जारी रखता है। हां, कुंजी स्थिर है, लेकिन एक विशिष्ट वस्तु के साथ जुड़ा हुआ है, वस्तु स्वयं वैश्विक नहीं है।
एलेक्स जूल

15

जू द्वारा बताया गया समाधान मूल्य प्रकारों का समर्थन नहीं करता है , यह उनके साथ भी ठीक काम करता है

रैपर

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

यह वास्तव में इतना बड़ा समाधान है, मैं एक और उपयोग उदाहरण जोड़ना चाहता था जिसमें संरचना और मूल्य शामिल थे जो वैकल्पिक नहीं हैं। साथ ही, एसोसिएटेडके वैल्यू को सरल बनाया जा सकता है।

struct Crate {
    var name: String
}

class Box {
    var name: String

    init(name: String) {
        self.name = name
    }
}

extension UIViewController {

    private struct AssociatedKey {
        static var displayed:   UInt8 = 0
        static var box:         UInt8 = 0
        static var crate:       UInt8 = 0
    }

    var displayed: Bool? {
        get {
            return getAssociatedObject(self, associativeKey: &AssociatedKey.displayed)
        }

        set {
            if let value = newValue {
                setAssociatedObject(self, value: value, associativeKey: &AssociatedKey.displayed, policy: objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            }
        }
    }

    var box: Box {
        get {
            if let result:Box = getAssociatedObject(self, associativeKey: &AssociatedKey.box) {
                return result
            } else {
                let result = Box(name: "")
                self.box = result
                return result
            }
        }

        set {
            setAssociatedObject(self, value: newValue, associativeKey: &AssociatedKey.box, policy: objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }

    var crate: Crate {
        get {
            if let result:Crate = getAssociatedObject(self, associativeKey: &AssociatedKey.crate) {
                return result
            } else {
                let result = Crate(name: "")
                self.crate = result
                return result
            }
        }

        set {
            setAssociatedObject(self, value: newValue, associativeKey: &AssociatedKey.crate, policy: objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
}

और मैं लिफ्टेड क्लास का उपयोग कैसे करूं?
एल्विस

आपको इसकी आवश्यकता क्यों है? किस लिए?
HepaKKes

मेरा मतलब है कि आप इसे सामान्य रूप से कैसे उपयोग करते हैं, आपकी मदद के लिए धन्यवाद :) क्या आप "उदाहरण का उपयोग कैसे करें" जोड़ सकते हैं, कृपया?
एल्विस सिप

1
"कैसे उपयोग करें" उदाहरण "एक संभावित क्लास एक्सटेंशन" हेडर के ठीक नीचे शुरू होगा। मुझे नहीं लगता कि उपयोगकर्ताओं को यह जानने की जरूरत है कि liftविधि और Liftedवर्ग का उपयोग कैसे किया जाए , हालांकि उन्हें हालांकि getAssociatedObjectऔर setAssociatedObjectकार्यों का उपयोग करना चाहिए । मैं स्पष्टता के लिए शीर्ष लेख के बगल में कोष्ठक के बीच "उपयोग का उदाहरण" जोड़ूंगा।
HepaKKes

1
यह निश्चित रूप से सबसे अच्छा समाधान है, बहुत बुरा यह बहुत अच्छी तरह से समझाया नहीं है। यह कक्षाओं, संरचनाओं और अन्य मूल्य प्रकारों के साथ काम करता है। गुणों को वैकल्पिक होने की जरूरत नहीं है। अच्छा काम।
पिकनिक

13

आप नए भंडारण के साथ श्रेणियों (स्विफ्ट एक्सटेंशन) को परिभाषित नहीं कर सकते हैं; किसी भी अतिरिक्त गुणों को संगृहीत करने के बजाय गणना की जानी चाहिए । सिंटैक्स ऑब्जेक्टिव सी के लिए काम करता है क्योंकि @propertyएक श्रेणी में अनिवार्य रूप से इसका मतलब है "मैं गेट्टर और सेटर प्रदान करूंगा"। स्विफ्ट में, आपको एक संगणित संपत्ति प्राप्त करने के लिए खुद को परिभाषित करने की आवश्यकता होगी; कुछ इस तरह:

extension String {
    public var Foo : String {
        get
        {
            return "Foo"
        }

        set
        {
            // What do you want to do here?
        }
    }
}

ठीक काम करना चाहिए। याद रखें, आप सेटर में नए मान स्टोर नहीं कर सकते हैं, केवल मौजूदा उपलब्ध वर्ग स्थिति के साथ काम कर सकते हैं।


7

मेरी $ 0.02। यह कोड स्विफ्ट 2.0 में लिखा गया है

extension CALayer {
    private struct AssociatedKeys {
        static var shapeLayer:CAShapeLayer?
    }

    var shapeLayer: CAShapeLayer? {
        get {
            return objc_getAssociatedObject(self, &AssociatedKeys.shapeLayer) as? CAShapeLayer
        }
        set {
            if let newValue = newValue {
                objc_setAssociatedObject(self, &AssociatedKeys.shapeLayer, newValue as CAShapeLayer?, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            }
        }
    }
}

मैंने कई समाधानों की कोशिश की है, और पाया कि यह वास्तव में अतिरिक्त चर मापदंडों के साथ एक वर्ग का विस्तार करने का एकमात्र तरीका है।


दिलचस्प लग रहा है, लेकिन प्रोटोकॉल के साथ काम करने से इनकार नहीं करता है, type 'AssociatedKeys' cannot be defined within a protocol extension...
इयान बायटेच

6

Objc रनटाइम पर भरोसा क्यों? मुझे बात नहीं सूझी। निम्नलिखित की तरह कुछ का उपयोग करके आप एक संग्रहीत संपत्ति के लगभग समान व्यवहार को प्राप्त करेंगे, केवल एक शुद्ध स्विफ्ट दृष्टिकोण का उपयोग करके :

extension UIViewController {
    private static var _myComputedProperty = [String:Bool]()

    var myComputedProperty:Bool {
        get {
            let tmpAddress = String(format: "%p", unsafeBitCast(self, to: Int.self))
            return UIViewController._myComputedProperty[tmpAddress] ?? false
        }
        set(newValue) {
            let tmpAddress = String(format: "%p", unsafeBitCast(self, to: Int.self))
            UIViewController._myComputedProperty[tmpAddress] = newValue
        }
    }
}

1
चतुर! इस दृष्टिकोण के साथ एक संभावित नकारात्मक पहलू स्मृति प्रबंधन है। मेज़बान वस्तु को हटा दिए जाने के बाद, यह संपत्ति अभी भी शब्दकोश में मौजूद होगी जो संभावित रूप से मेमोरी उपयोग पर महंगा हो सकती है यदि यह बहुत अधिक वस्तुओं के साथ उपयोग किया जाता है।
चार्लटन प्रोवाटास

संभवतः हम रैपरों की एक स्थिर सूची चर को पकड़ सकते हैं जिसमें वस्तुओं (स्वयं) का कमजोर संदर्भ होता है। और _myComputedProperty डिक्शनरी से रैपर के रैपर लिस्ट में रैपर के रैपर को हटाएं (रैपर में ऑब्जेक्ट रिजेक्ट हो जाता है)
अर्जुन

5

मैं शुद्ध स्विफ्ट में कोड करना पसंद करता हूं और उद्देश्य-सी विरासत पर भरोसा नहीं करता। इसकी वजह से मैंने दो फायदे और दो नुकसान के साथ शुद्ध स्विफ्ट समाधान लिखा।

लाभ:

  1. शुद्ध स्विफ्ट कोड

  2. कक्षाओं और पूर्णताओं या अधिक विशेष रूप से Anyऑब्जेक्ट पर काम करता है

नुकसान:

  1. कोड को willDeinit()मेमोरी लीक से बचने के लिए विशिष्ट वर्ग उदाहरण से जुड़ी वस्तुओं को जारी करने के लिए विधि को कॉल करना चाहिए

  2. आप इस सटीक उदाहरण के लिए सीधे UIView के लिए एक्सटेंशन नहीं कर सकते क्योंकि UIView के लिए var frameविस्तार है, वर्ग का हिस्सा नहीं है।

संपादित करें:

import UIKit

var extensionPropertyStorage: [NSObject: [String: Any]] = [:]

var didSetFrame_ = "didSetFrame"

extension UILabel {

    override public var frame: CGRect {

        get {
            return didSetFrame ?? CGRectNull
        }

        set {
            didSetFrame = newValue
        }
    }

    var didSetFrame: CGRect? {

        get {
            return extensionPropertyStorage[self]?[didSetFrame_] as? CGRect
        }

        set {
            var selfDictionary = extensionPropertyStorage[self] ?? [String: Any]()

            selfDictionary[didSetFrame_] = newValue

            extensionPropertyStorage[self] = selfDictionary
        }
    }

    func willDeinit() {
        extensionPropertyStorage[self] = nil
    }
}

4
यह एक्सटेंशनप्रोपरस्टीस्टोरेज के सभी उदाहरणों के बीच साझा किए जाने के बाद से काम नहीं करता है। यदि आप एक उदाहरण के लिए मान सेट करते हैं, तो आप सभी उदाहरणों के लिए मान सेट कर रहे हैं।
22

यह युद्ध अच्छा नहीं है क्योंकि कार्यों के साथ खिलवाड़। अब यह बेहतर है और इरादा के अनुसार काम करता है।
vedrano

@picciano extensionPropertyStorage को डिज़ाइन द्वारा सभी उदाहरणों के साथ साझा किया गया है। यह ग्लोबल वैरिएबल (डिक्शनरी) है जो पहले यू-बेले उदाहरण ( NSObject) और फिर उसकी संपत्ति ( [String: Any]) है।
vedrano

@picciano मुझे लगता है कि यह classसंपत्तियों के लिए काम करता है ;)
निकोलस मिआरी

frameएक उदाहरण संपत्ति है। वर्ग संपत्ति कहाँ से आती है?
vedrano

3

ओब्ज-सी श्रेणियों के साथ आप केवल तरीके जोड़ सकते हैं, उदाहरण चर नहीं।

उदाहरण में, आपने गेटप्रेटर और सेटर विधि घोषणाओं को जोड़ने के शॉर्टकट के रूप में @property का उपयोग किया है। आपको अभी भी उन तरीकों को लागू करने की आवश्यकता है।

इसी तरह स्विफ्ट में आप इंस्टेंस मेथड्स जोड़ सकते हैं इंस्टेंस मेथड्स, कंप्यूटेड प्रॉपर्टीज आदि को जोड़ने के लिए लेकिन स्टोर किए हुए प्रॉपर्टीज में नहीं।


2

मैं भी में एक EXC_BAD_ACCESS problem.The मूल्य प्राप्त objc_getAssociatedObject()और objc_setAssociatedObject()एक वस्तु होना चाहिए। और objc_AssociationPolicyऑब्जेक्ट से मेल खाना चाहिए।


3
यह वास्तव में सवाल का जवाब नहीं देता है। यदि आपका कोई अलग प्रश्न है, तो आप प्रश्न पूछें पर क्लिक करके पूछ सकते हैं । पर्याप्त प्रतिष्ठा होने पर आप इस प्रश्न पर अधिक ध्यान आकर्षित करने के लिए एक इनाम भी जोड़ सकते हैं । - समीक्षा से
नास्तिक ३

@ AtheistP3ace को लेकर मुझे बहुत अफ़सोस है। मैं प्रश्न में एक टिप्पणी जोड़ने की कोशिश करता हूं। लेकिन मेरे पास पर्याप्त प्रतिष्ठा नहीं है। इसलिए मैं सवाल का जवाब देने की कोशिश करता हूं।
रुईक्यूक्यू

2

मैंने objc_setAssociatedObject का उपयोग करने की कोशिश की जैसा कि यहां कुछ उत्तरों में उल्लेख किया गया है, लेकिन इसके साथ कुछ बार असफल होने के बाद मैं पीछे हट गया और महसूस किया कि ऐसा कोई कारण नहीं है जिसकी मुझे आवश्यकता है। यहाँ कुछ विचारों से दुखी होकर, मैं इस कोड के साथ आया, जो मेरे अतिरिक्त डेटा (इस उदाहरण में MyClass) की एक सरणी को संग्रहीत करता है, जिस वस्तु को मैं इसके साथ जोड़ना चाहता हूँ, उसे अनुक्रमित:

class MyClass {
    var a = 1
    init(a: Int)
    {
        self.a = a
    }
}

extension UIView
{
    static var extraData = [UIView: MyClass]()

    var myClassData: MyClass? {
        get {
            return UIView.extraData[self]
        }
        set(value) {
            UIView.extraData[self] = value
        }
    }
}

// Test Code: (Ran in a Swift Playground)
var view1 = UIView()
var view2 = UIView()

view1.myClassData = MyClass(a: 1)
view2.myClassData = MyClass(a: 2)
print(view1.myClassData?.a)
print(view2.myClassData?.a)

क्या आपके पास कोई विचार है कि कैसे एक्स्ट्राटाटा को स्वचालित रूप से साफ़ करें ताकि यह मेमोरी लीक का कारण न बने?
DoubleK

मैं वास्तव में विचारों के साथ इसका उपयोग नहीं कर रहा था, मेरे मामले में मैं कभी भी कक्षा में उपयोग की जाने वाली वस्तुओं की एक सीमित संख्या का तुरंत उपयोग करता हूं, इसलिए मैंने वास्तव में मेमोरी लीक नहीं माना है। मैं ऐसा करने के लिए एक स्वचालित तरीके के बारे में नहीं सोच सकता हूं, लेकिन मुझे लगता है कि ऊपर के सेटर में आप तत्व को निकालने के लिए तर्क जोड़ सकते हैं यदि मान == शून्य हो, और तब मान को शून्य पर सेट करें जब आपको ऑब्जेक्ट की आवश्यकता नहीं होती है, या शायद। में कुछ भी।
डैन

Tnx @Dan, मैंने शून्य पर मान सेट करने के लिए viewWillDisapper का उपयोग किया और यह ठीक काम करता है, अब डिनिट कहा जाता है और सब कुछ ठीक काम करता है। इस समाधान को पोस्ट करने के लिए Tnx
DoubleK

2

यहाँ सरलीकृत और अधिक अभिव्यंजक समाधान है। यह मूल्य और संदर्भ प्रकार दोनों के लिए काम करता है। उठाने का दृष्टिकोण @HepaKKes उत्तर से लिया गया है।

एसोसिएशन कोड:

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 associated<T>(to base: AnyObject,
                key: UnsafePointer<UInt8>,
                policy: objc_AssociationPolicy = .OBJC_ASSOCIATION_RETAIN,
                initialiser: () -> T) -> T {
    if let v = objc_getAssociatedObject(base, key) as? T {
        return v
    }

    if let v = objc_getAssociatedObject(base, key) as? Lifted<T> {
        return v.value
    }

    let lifted = Lifted(initialiser())
    objc_setAssociatedObject(base, key, lifted, policy)
    return lifted.value
}

func associate<T>(to base: AnyObject, key: UnsafePointer<UInt8>, value: T, policy: objc_AssociationPolicy = .OBJC_ASSOCIATION_RETAIN) {
    if let v: AnyObject = value as AnyObject? {
        objc_setAssociatedObject(base, key, v, policy)
    }
    else {
        objc_setAssociatedObject(base, key, lift(value), policy)
    }
}

उपयोग का उदाहरण:

1) इसके लिए एक्सटेंशन और सहयोगी गुण बनाएं। मान और संदर्भ दोनों प्रकार के गुणों का उपयोग करते हैं।

extension UIButton {

    struct Keys {
        static fileprivate var color: UInt8 = 0
        static fileprivate var index: UInt8 = 0
    }

    var color: UIColor {
        get {
            return associated(to: self, key: &Keys.color) { .green }
        }
        set {
            associate(to: self, key: &Keys.color, value: newValue)
        }
    }

    var index: Int {
        get {
            return associated(to: self, key: &Keys.index) { -1 }
        }
        set {
            associate(to: self, key: &Keys.index, value: newValue)
        }
    }

}

2) अब आप नियमित गुणों के रूप में उपयोग कर सकते हैं:

    let button = UIButton()
    print(button.color) // UIExtendedSRGBColorSpace 0 1 0 1 == green
    button.color = .black
    print(button.color) // UIExtendedGrayColorSpace 0 1 == black

    print(button.index) // -1
    button.index = 3
    print(button.index) // 3

अधिक जानकारी:

  1. मूल्य प्रकारों को लपेटने के लिए भार उठाने की आवश्यकता होती है।
  2. डिफ़ॉल्ट संबद्ध ऑब्जेक्ट व्यवहार बनाए रखा गया है। यदि आप संबंधित वस्तुओं के बारे में अधिक जानना चाहते हैं, तो मैं इस लेख की जाँच करने की सलाह दूंगा ।

1

उद्देश्य-सी से जुड़ी वस्तुओं और स्विफ्ट 3 और स्विफ्ट 4 के लिए गणना की गई संपत्तियों का उपयोग करने के साथ एक और उदाहरण

import CoreLocation

extension CLLocation {

    private struct AssociatedKeys {
        static var originAddress = "originAddress"
        static var destinationAddress = "destinationAddress"
    }

    var originAddress: String? {
        get {
            return objc_getAssociatedObject(self, &AssociatedKeys.originAddress) as? String
        }
        set {
            if let newValue = newValue {
                objc_setAssociatedObject(
                    self,
                    &AssociatedKeys.originAddress,
                    newValue as NSString?,
                    .OBJC_ASSOCIATION_RETAIN_NONATOMIC
                )
            }
        }
    }

    var destinationAddress: String? {
        get {
            return objc_getAssociatedObject(self, &AssociatedKeys.destinationAddress) as? String
        }
        set {
            if let newValue = newValue {
                objc_setAssociatedObject(
                    self,
                    &AssociatedKeys.destinationAddress,
                    newValue as NSString?,
                    .OBJC_ASSOCIATION_RETAIN_NONATOMIC
                )
            }
        }
    }

}

1

यदि आप एक UIView के लिए एक कस्टम स्ट्रिंग विशेषता सेट करना चाह रहे हैं, तो यह है कि मैंने इसे स्विफ्ट 4 पर कैसे किया

UIView एक्सटेंशन बनाएं

extension UIView {

    func setStringValue(value: String, key: String) {
        layer.setValue(value, forKey: key)
    }

    func stringValueFor(key: String) -> String? {
        return layer.value(forKey: key) as? String
    }
}

इस विस्तार का उपयोग करने के लिए

let key = "COLOR"

let redView = UIView() 

// To set
redView.setStringAttribute(value: "Red", key: key)

// To read
print(redView.stringValueFor(key: key)) // Optional("Red")

1

स्थैतिक मानचित्र को उस वर्ग में कैसे संग्रहीत किया जाए जो इस तरह से विस्तारित हो रहा है:

extension UIView {

    struct Holder {
        static var _padding:[UIView:UIEdgeInsets] = [:]
    }

    var padding : UIEdgeInsets {
        get{ return UIView.Holder._padding[self] ?? .zero}
        set { UIView.Holder._padding[self] = newValue }
    }

}

1
इस उत्तर का कोई उत्तर नहीं है। आखिर स्मार्ट समाधान।
byJeevan

यह काम करता है लेकिन मुझे लगता है के रूप में यहां कहा गया है इस दृष्टिकोण त्रुटिपूर्ण medium.com/@valv0/...
टेफी

0

मैंने बिना किसी भाग्य के objc_getAssociatedObject, objc_setAssociatedObject का उपयोग करके गुणों को संग्रहीत करने का प्रयास किया। टेक्स्ट इनपुट वर्णों की लंबाई को मान्य करने के लिए मेरा लक्ष्य UITextField के लिए एक्सटेंशन बनाया गया था। निम्नलिखित कोड मेरे लिए ठीक काम करता है। उम्मीद है कि यह किसी की मदद करेगा।

private var _min: Int?
private var _max: Int?

extension UITextField {    
    @IBInspectable var minLength: Int {
        get {
            return _min ?? 0
        }
        set {
            _min = newValue
        }
    }

    @IBInspectable var maxLength: Int {
        get {
            return _max ?? 1000
        }
        set {
            _max = newValue
        }
    }

    func validation() -> (valid: Bool, error: String) {
        var valid: Bool = true
        var error: String = ""
        guard let text = self.text else { return (true, "") }

        if text.characters.count < minLength {
            valid = false
            error = "Textfield should contain at least \(minLength) characters"
        }

        if text.characters.count > maxLength {
            valid = false
            error = "Textfield should not contain more then \(maxLength) characters"
        }

        if (text.characters.count < minLength) && (text.characters.count > maxLength) {
            valid = false
            error = "Textfield should contain at least \(minLength) characters\n"
            error = "Textfield should not contain more then \(maxLength) characters"
        }

        return (valid, error)
    }
}

वैश्विक निजी चर? आपको लगता है कि एहसास है _minऔर _maxवैश्विक हैं और UITextField के सभी उदाहरणों में एक ही हो जाएगा? यहां तक ​​कि अगर यह आपके लिए काम करता है, तो यह उत्तर असंबंधित है क्योंकि मार्कोस उदाहरण के चर के बारे में पूछ रहे हैं ।
केलिन

हाँ, आप सही हैं, यह सभी उदाहरणों के लिए काम नहीं कर रहा है। मैं संरचना में न्यूनतम और अधिकतम मूल्य संग्रहीत करके फिक्स्ड। ऑफ विषय के लिए क्षमा करें।
वादिम्स क्रूटोव्स

0

यहाँ एक विकल्प है जो काम भी करता है

public final class Storage : AnyObject {

    var object:Any?

    public init(_ object:Any) {
        self.object = object
    }
}

extension Date {

    private static let associationMap = NSMapTable<NSString, AnyObject>()
    private struct Keys {
        static var Locale:NSString = "locale"
    }

    public var locale:Locale? {
        get {

            if let storage = Date.associationMap.object(forKey: Keys.Locale) {
                return (storage as! Storage).object as? Locale
            }
            return nil
        }
        set {
            if newValue != nil {
                Date.associationMap.setObject(Storage(newValue), forKey: Keys.Locale)
            }
        }
    }
}



var date = Date()
date.locale = Locale(identifier: "pt_BR")
print( date.locale )

-1

मुझे यह समाधान अधिक व्यावहारिक लगा

स्विफ्ट 3 के लिए अद्यतन

extension UIColor {

    static let graySpace = UIColor.init(red: 50/255, green: 50/255, blue: 50/255, alpha: 1.0)
    static let redBlood = UIColor.init(red: 102/255, green: 0/255, blue: 0/255, alpha: 1.0)
    static let redOrange = UIColor.init(red: 204/255, green: 17/255, blue: 0/255, alpha: 1.0)

    func alpha(value : CGFloat) -> UIColor {
        var r = CGFloat(0), g = CGFloat(0), b = CGFloat(0), a = CGFloat(0)
        self.getRed(&r, green: &g, blue: &b, alpha: &a)
        return UIColor(red: r, green: g, blue: b, alpha: value)
    }

}

... तो आपके कोड में

class gameController: UIViewController {

    @IBOutlet var game: gameClass!

    override func viewDidLoad() {
        self.view.backgroundColor = UIColor.graySpace

    }
}

यह इसे करने का सही तरीका है - लेकिन वास्तव में एक्सटेंशन में संग्रहीत संपत्ति नहीं है जैसे कि सवाल पूछता है।
UKDataGeek
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.