Suche…


Eigenschaft in einer Protokollerweiterung, die mithilfe des verknüpften Objekts erzielt wird.

In Swift können Protokollerweiterungen keine echten Eigenschaften haben.

In der Praxis können Sie jedoch die Technik "Zugeordnetes Objekt" verwenden. Das Ergebnis ist fast genau wie eine "echte" Eigenschaft.

Hier ist die genaue Technik zum Hinzufügen eines "assoziierten Objekts" zu einer Protokollerweiterung:

Grundsätzlich verwenden Sie die Aufrufe Objective-c "objc_getAssociatedObject" und _set.

Die grundlegenden Aufrufe sind:

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

Hier ist ein vollständiges Beispiel. Die zwei kritischen Punkte sind:

  1. Im Protokoll müssen Sie ": class" verwenden, um das Mutationsproblem zu vermeiden.

  2. In der Erweiterung müssen Sie "where Self: UIViewController" (oder eine geeignete Klasse) verwenden, um den Bestätigungstyp anzugeben.

Also für eine beispielhafte Eigenschaft "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
}

In jeder konformen Klasse haben Sie jetzt die Eigenschaft "p" hinzugefügt:

Sie können "p" genauso verwenden, wie Sie eine gewöhnliche Eigenschaft in der konformen Klasse verwenden würden. Beispiel:

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

Hinweis. Sie MÜSSEN die Pseudo-Eigenschaft initialisieren.

Xcode erzwingt nicht das Initialisieren von "p" in der konformen Klasse.

Es ist wichtig, dass Sie "p" initialisieren, möglicherweise in viewDidLoad der bestätigenden Klasse.

Es sei daran erinnert, dass p eigentlich nur eine berechnete Eigenschaft ist . p ist eigentlich nur zwei Funktionen mit syntaktischem Zucker. Es gibt nirgends eine "Variable" von p: Der Compiler weist "keinen Speicherplatz für p" zu. Aus diesem Grund ist es sinnlos zu erwarten, dass Xcode "Initialisieren von p" erzwingt.

Um genauer zu sprechen, müssen Sie unbedingt daran denken, "p zum ersten Mal zu verwenden, als ob Sie es initialisieren würden". (Auch dies wäre sehr wahrscheinlich in Ihrem viewDidLoad-Code enthalten.)

Betrachtet man den Getter als solchen.

Beachten Sie, dass es abstürzt, wenn der Getter aufgerufen wird, bevor ein Wert für "p" festgelegt wird.

Um dies zu vermeiden, betrachten Sie folgenden Code:

    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
        }

Wiederholen. Xcode erzwingt keine Initialisierung von p in der konformen Klasse. Es ist wichtig, dass Sie p initialisieren, beispielsweise in viewDidLoad der entsprechenden Klasse.

Code einfacher machen ...

Möglicherweise möchten Sie diese beiden globalen Funktionen verwenden:

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

Beachten Sie, dass sie nichts tun, außer das Schreiben zu speichern und den Code lesbarer zu machen. (Sie sind im Wesentlichen Makros oder Inline-Funktionen.)

Ihr Code wird dann zu:

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

(Im Beispiel bei _aoGet ist P initialisierbar: Anstelle von P () können Sie "", 0 oder einen beliebigen Standardwert verwenden.)



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow