Objective-Cで行ったのと同じように、Swiftにプロパティを保存するにはどうすればよいですか?


132

アプリケーションをObjective-CからSwiftに切り替えます。これには、保存されたプロパティを持ついくつかのカテゴリがあります。次に例を示します。

@interface UIView (MyCategory)

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

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

@end

Swift拡張機能はこれらのような格納されたプロパティを受け入れないため、Objcコードと同じ構造を維持する方法がわかりません。保存されたプロパティは私のアプリにとって本当に重要であり、AppleはSwiftでそれを行うためのいくつかのソリューションを作成したに違いないと思います。

jouが言ったように、私が探していたのは実際には関連オブジェクトを使用することだったので、別のコンテキストで使用しました。

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
Objective-Cクラスのカテゴリでもインスタンス変数を定義できないので、それらのプロパティをどのように実現しましたか
Martin R

なぜ悪いアクセス権を取得するのかはわかりませんが、コードは機能しません。setAssociateObjectとgetAssociatedObjectに異なる値を渡しています。文字列「shapeLayer」をキーとして使用するのは間違っています。キーであるのはポインタ(実際にはアドレス)であり、それが指すものではありません。異なるアドレスにある2つの同一の文字列は、2つの異なるキーです。Jouの回答を確認し、xoAssociationKeyがどのようにグローバル変数として定義されているかを確認してください。これにより、設定/取得時に同じキー/ポインターになります。
SafeFastExpressive 2014年

回答:


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

より読みやすい方法でプロパティをobjective-cクラスに「追加」できる場合:

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

Int、Boolなどを保存したい場合はどうすればよいですか?
Vyachaslav Gerchicov 2017年

1
オブジェクトの関連付けを介してSwiftタイプを直接格納することはできません。たとえばNSNumber、格納したりNSValue、必要なタイプ(Int、Boolなど)の追加のアクセサペアを記述したりできます。
Wojciech Nagrodzki 2017

1
警告これは構造体では機能しません。なぜなら、objective-cランタイムライブラリは、準拠するクラスのみをサポートするためですNSObjectProtocol
Charlton Provatas

2
@CharltonProvatasこのAPIで構造体を使用することはできません。構造体はAnyObjectプロトコルに準拠していません。
Wojciech Nagrodzki

@VyachaslavGerchicov [String]?は構造体です。これはInt、Boolの場合と同じです。インスタンスのNSArrayコレクションを保持するために使用できNSStringます。
Wojciech Nagrodzki

184

Objective-Cと同様に、保存されたプロパティを既存のクラスに追加することはできません。Objective-Cクラス(UIView間違いなく1つ)を拡張する場合でも、関連付けられたオブジェクトを使用して、格納されたプロパティをエミュレートできます。

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

アソシエーションキーは、アソシエーションごとに一意である必要があるポインタです。そのために、プライベートグローバル変数を作成し、そのメモリアドレスを&演算子のキーとして使用します。Swiftでポインターを処理する方法の詳細については、CocoaおよびObjective-CでのSwift使用を 参照してください。

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

Swift 4用に更新

Swift 4では、はるかに簡単です。Holder構造体には、計算されたプロパティが世界に公開するプライベート値が含まれ、代わりに格納されたプロパティの動作のように見えます。

ソース

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

2
@Yarについて知りませんobjc_AssociationPolicyでした、ありがとう!答えを更新しました
jou

2
Swift2では、objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN
Tom

1
@SimplGyコードを他の人の答えに編集するのではなく、編集を追加の答えとして追加してください。
JAL

11
拡張機能のプロパティを使用するUIViewControllerのインスタンスを複数持つ場合、Swift4ソリューションは機能しません。ソースの更新を確認してください。
1

9
Swift 4の例は複数のインスタンスでは機能せず、おそらくここにあるべきではありません。
Colin Cornaby

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

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

@AKWeblS私はあなたのようなコードを実装しましたが、swift 2.0に更新すると、タイプ「objc_AssociationPolicy)」の引数リストを使用してタイプ「UInt」のイニシャライザを呼び出せないというエラーが発生します。次のコメントのコード
user2363025

Swift 2.0のアップデート方法は?var postDescription:String?{get {return objc_getAssociatedObject(self、&AssociatedKeys.postDescription)as?String} set {if let newValue = newValue {objc_setAssociatedObject(self、&AssociatedKeys.postDescription、newValue as NSString ?, UInt(objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC))}}}
user2363025

1
動作しません:静的変数は、プロパティの値がすべてのインスタンスで同じであることを意味します
Fry

@fry-それは私のために働き続けます。はい、キーは静的ですが、特定のオブジェクトに関連付けられています。オブジェクト自体はグローバルではありません。
AlexK 2017

15

jouによって指摘されたソリューションは値タイプをサポートしていません。これはそれらでもうまく機能します

ラッパー

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

これは本当に素晴らしいソリューションです。オプションではない構造体と値を含む別の使用例を追加したいと思いました。また、AssociatedKey値を簡略化できます。

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

また、Liftedクラスはどのように使用しますか?
3lvis 2015

なぜそれが必要なのですか?何のために?
HepaKKes

私はあなたの助けに感謝して、これを一般的にどのように使用するかを意味しました:)「使用方法」の例を追加できますか?
3lvis

1
「使用方法」の例は、「可能なクラス拡張」ヘッダーのすぐ下から始まります。ユーザーはliftメソッドとLiftedクラスの使い方を知っている必要はないと思います。ただし、getAssociatedObjectsetAssociatedObject関数を使用する必要があります。わかりやすくするために、ヘッダーの横の括弧の間に「使用例」を追加します。
HepaKKes

1
これは確かに最良の解決策ですが、あまりにもよく説明されていないので残念です。クラス、構造体、およびその他の値型で機能します。プロパティもオプションである必要はありません。よくやった。
picciano 2016

13

新しいストレージでカテゴリ(Swift拡張)を定義することはできません。追加のプロパティは、保存するのではなく計算する必要があります。@propertyカテゴリでは本質的に「ゲッターとセッターを提供する」という意味なので、構文はObjective Cで機能します。Swiftでは、計算されたプロパティを取得するために、これらを自分で定義する必要があります。何かのようなもの:

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

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

正常に動作するはずです。新しい値をセッターに保存することはできません。既存の利用可能なクラスの状態でのみ機能します。


7

私の0.02ドル。このコードはSwift 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...
Ian Bytchek

6

なぜobjcランタイムに依存するのですか?ポイントがわかりません。次のようなものを使用することにより、純粋なSwiftアプローチのみを使用して、保存されたプロパティとほぼ同じ動作を実現できます。

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
賢い!このアプローチの欠点の1つは、メモリ管理です。ホストオブジェクトが割り当て解除された後も、そのプロパティはディクショナリに存在し続けます。これを多くのオブジェクトで使用すると、メモリ使用量が高くなる可能性があります。
Charlton Provatas

おそらく、オブジェクト(自身)への弱い参照を含むラッパーの静的リスト変数を保持できます。そして、_myComputedPropertyディクショナリからエントリを削除し、WrapperのdidSetメソッドでラッパーの静的リスト変数を削除します(オブジェクトが再割り当てされると、Wrapperクラス内のdidSetが呼び出されます。これは、弱参照にnilで新しい値が設定されているためです)。
アルジュナ

5

私はObjective-Cの遺産に依存せず、純粋なSwiftでコードを実行することを好みます。このため、2つの利点と2つの欠点を持つ純粋なSwiftソリューションを作成しました。

利点:

  1. ピュアスイフトコード

  2. クラスとコンプリーション、より具体的にはAnyオブジェクトで機能します

短所:

  1. コードはメソッドwillDeinit()を呼び出して、特定のクラスインスタンスにリンクされているオブジェクトを解放し、メモリリークを回避する必要があります

  2. var frameは、クラスの一部ではなくUIViewの拡張であるため、この正確な例では、UIViewを直接拡張することはできません。

編集:

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
extensionPropertyStorageはすべてのインスタンスで共有されるため、これは機能しません。1つのインスタンスに値を設定すると、すべてのインスタンスに値が設定されます。
picciano 2016

関数をめちゃくちゃにするので良くない戦争。今ではそれはより良く、意図したとおりに機能します。
vedrano 2016

@picciano extensionPropertyStorage は、設計によりすべてのインスタンスと共有されます。これは、最初にUILabelインスタンス(NSObject)を逆参照し、次にそのプロパティ([String: Any])を逆参照するグローバル変数(Dictionary)です。
vedrano 2016

@piccianoこれはclassプロパティで機能すると思います;)
Nicolas

frameインスタンスプロパティです。クラスプロパティはどこから来るのですか?
vedrano 2016

3

Obj-cカテゴリーでは、インスタンス変数ではなく、メソッドのみを追加できます。

あなたの例では、ゲッターとセッターのメソッド宣言を追加するためのショートカットとして@propertyを使用しています。これらのメソッドを実装する必要があります。

同様に、Swiftでは、拡張機能を使用してインスタンスメソッドや計算されたプロパティなどを追加できますが、保存されているプロパティは追加できません。


2

私はまた、EXC_BAD_ACCESS problem.The値を取得objc_getAssociatedObject()し、objc_setAssociatedObject()オブジェクトでなければなりません。そして、objc_AssociationPolicyオブジェクトと一致する必要があります。


3
これは実際には質問の答えにはなりません。別の質問がある場合は、[ 質問する]をクリックして質問できます。賞金追加して、十分な評判を得たら、この質問にもっと注意を向けることもできます。- レビューから
AtheistP3ace

@ AtheistP3aceごめんなさい。質問にコメントを付けようとしていますが、評判が足りません。だから私は質問に答えようとします。
RuiKQ 2016

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)

メモリリークを引き起こさないように、extraDataを自動的にクリアする方法を知っていますか?
DoubleK 2016

私は実際にこれをビューで使用していませんでした。私の場合は、使用しているクラスの有限数のオブジェクトのみをインスタンス化するため、メモリリークについては考慮していません。これを自動的に行う方法は考えられませんが、上記のセッターでは、value == nilの場合に要素を削除するロジックを追加し、オブジェクトが不要になったときに値をnilに設定できると思います。多分didReceiveMemoryWarningか何かで。
Dan

Tnx @Dan、私はviewWillDisapperを使用して値をnilに設定しましたが、正常に機能します。これでdeinitが呼び出され、すべてが正常に機能します。このソリューションを投稿するためのTnx
DoubleK 2016年

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

Objective-Cに関連付けられたオブジェクトと、Swift 3およびSwift 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に設定する場合は、これをSwift 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
なぜこの回答には賛成票がないのですか。結局のところ、スマートなソリューションです。
Jeevan

この作品は、私は、ここで述べたように、このアプローチは欠陥があると思います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のすべてのインスタンスで同じになりますか?たとえそれがあなたのために働くとしても、マルコスがインスタンス変数について尋ねるので、この答えは無関係です。
kelin

はい、そうです、これはすべてのインスタンスで機能するわけではありません。構造体に最小値と最大値を保存することで修正しました。オフトピックでごめんなさい。
Vadims Krutovs 2016

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

私はこの解決策がより実用的だと思いました

Swift 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 2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.