サーチ…


関連するオブジェクトを使用して達成されるプロトコル拡張のプロパティ。

Swiftでは、プロトコル拡張は真の特性を持つことができません。

しかし、実際には、「関連オブジェクト」技術を使用することができます。結果は「真の」特性とほぼ同じです。

プロトコル拡張に「関連オブジェクト」を追加するための正確な手法は次のとおりです。

基本的には、objective-c "objc_getAssociatedObject"と_set呼び出しを使用します。

基本的な呼び出しは次のとおりです。

get {
   return objc_getAssociatedObject(self, & _Handle) as! YourType
   }
set {
   objc_setAssociatedObject(self, & _Handle, newValue, .OBJC_ASSOCIATION_RETAIN)
    }

ここに完全な例があります。 2つの重要なポイントは次のとおりです。

  1. このプロトコルでは、 ":class"を使用して突然変異の問題を回避する必要があります。

  2. エクステンションでは、「どこで:UIViewController」(または適切なクラス)を使用して確認タイプを指定する必要があります。

したがって、プロパティ "p"の例では、次のようになります。

import Foundation
import UIKit
import ObjectiveC          // don't forget this

var _Handle: UInt8 = 42    // it can be any value

protocol Able: class {
    var click:UIView? { get set }
    var x:CGFloat? { get set }
    // note that you >> do not << declare p here
}

extension Able where Self:UIViewController {
       
    var p:YourType { // YourType might be, say, an Enum
      get {
          return objc_getAssociatedObject(self, & _Handle) as! YourType
          // HOWEVER, SEE BELOW
          }
      set {
          objc_setAssociatedObject(self, & _Handle, newValue, .OBJC_ASSOCIATION_RETAIN)
          // often, you'll want to run some sort of "setter" here...
          __setter()
          }
    }
    
    func __setter() { something = p.blah() }
    
    func someOtherExtensionFunction() { p.blah() }
    // it's ok to use "p" inside other extension functions,
    // and you can use p anywhere in the conforming class
}

適合するクラスでは、プロパティ "p"を "追加"しました。

適合クラスで普通のプロパティを使うのと同じように "p"を使うことができます。例:

class Clock:UIViewController, Able {
    var u:Int = 0

    func blah() {
      u = ...
      ... = u
      // use "p" as you would any normal property
      p = ...
      ... = p
    }

    override func viewDidLoad() {
      super.viewDidLoad()
      pm = .none // "p" MUST be "initialized" somewhere in Clock 
    }
}

注意。擬似プロパティを初期化する必要があります。

Xcode 、準拠しているクラスで "p"を初期化するよう強制しません

確認クラスのviewDidLoadの中で、おそらく "p"を初期化する必要があります。

pは実際には計算されたプロパティであることを覚えておく価値があります 。 pは実際には構文的な砂糖を含む2つの関数です。 p "変数"はどこにもありません。コンパイラはどのような意味でもpにいくつかのメモリを割り当てません。このため、Xcodeが「pを初期化する」ことを期待することは無意味です。

確かに、より正確に言えば、「初期化したかのように、初めてpを使用する」ことを覚えておく必要があります。 (再び、あなたのviewDidLoadコードの中にある可能性が高いです。)

そのようなゲッターについて。

"p"の値が設定される前にgetterが呼び出されると、 クラッシュすることに注意してください。

これを避けるには、次のようなコードを検討してください。

    get {
        let g = objc_getAssociatedObject(self, &_Handle)
        if (g == nil) {
            objc_setAssociatedObject(self, &_Handle, _default initial value_, .OBJC_ASSOCIATION)
            return _default initial value_
        }
        return objc_getAssociatedObject(self, &_Handle) as! YourType
        }

繰り返す。 Xcode 、準拠しているクラスでpを初期化するよう強制しません 。適合するクラスのviewDidLoadでpを初期化することが不可欠です。

コードを簡単にする

これらの2つのグローバル関数を使用することができます:

func _aoGet(_ ss: Any!, _ handlePointer: UnsafeRawPointer!, _ safeValue: Any!)->Any! {
    let g = objc_getAssociatedObject(ss, handlePointer)
    if (g == nil) {
        objc_setAssociatedObject(ss, handlePointer, safeValue, .OBJC_ASSOCIATION_RETAIN)
        return safeValue
    }
    return objc_getAssociatedObject(ss, handlePointer)  
}

func _aoSet(_ ss: Any!, _ handlePointer: UnsafeRawPointer!, _ val: Any!) {
    objc_setAssociatedObject(ss, handlePointer, val, .OBJC_ASSOCIATION_RETAIN)
}

入力を省略してコードを読みやすくする以外に何もしないことに注意してください。 (それらは本質的にマクロまたはインライン関数です。)

あなたのコードは次のようになります:

protocol PMable: class {
    var click:UILabel? { get set } // ordinary properties here
}

var _pHandle: UInt8 = 321

extension PMable where Self:UIViewController {

    var p:P {
        get {
            return _aoGet(self, &_pHandle, P() ) as! P
        }
        set {
            _aoSet(self, &_pHandle, newValue)
            __pmSetter()
        }
    }
    
    func __pmSetter() {
        click!.text = String(p)
    }
    
    func someFunction() {
        p.blah()
    }
}

(_aoGetの例では、Pは初期化可能です:P()の代わりに ""、0、または任意のデフォルト値を使用できます)。



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow