Recherche…


Propriété, dans une extension de protocole, obtenue à l'aide d'un objet associé.

Dans Swift, les extensions de protocole ne peuvent pas avoir de vraies propriétés.

Cependant, dans la pratique, vous pouvez utiliser la technique "objet associé". Le résultat est presque exactement comme une "vraie" propriété.

Voici la technique exacte pour ajouter un "objet associé" à une extension de protocole:

Fondamentalement, vous utilisez les appels objective-c "objc_getAssociatedObject" et _set.

Les appels de base sont les suivants:

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

Voici un exemple complet. Les deux points critiques sont:

  1. Dans le protocole, vous devez utiliser ": class" pour éviter le problème de mutation.

  2. Dans l'extension, vous devez utiliser "Where Self: UIViewController" (ou toute autre classe appropriée) pour indiquer le type de confirmation.

Donc, pour un exemple de propriété "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
}

Dans toute classe conforme, vous avez maintenant "ajouté" la propriété "p":

Vous pouvez utiliser "p" comme vous utiliseriez n'importe quelle propriété ordinaire de la classe conforme. Exemple:

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

Remarque. Vous DEVEZ initialiser la pseudo-propriété.

Xcode ne vous imposera pas d'initialiser "p" dans la classe conforme.

Il est essentiel que vous initialisiez "p", peut-être dans viewDidLoad de la classe de confirmation.

Il convient de rappeler que p n'est en réalité qu'une propriété calculée . p n'est en réalité que deux fonctions, avec le sucre syntaxique. Il n'y a pas de p "variable" n'importe où: le compilateur "n'attribue pas de mémoire pour p" dans aucun sens. Pour cette raison, il est inutile de s'attendre à ce que Xcode impose "l'initialisation p".

En effet, pour parler plus précisément, vous devez vous rappeler d’utiliser "p pour la première fois, comme si vous l’initialisiez". (Encore une fois, ce serait très probablement dans votre code viewDidLoad.)

En ce qui concerne le getter en tant que tel.

Notez qu'il va planter si l'appel est appelé avant qu'une valeur pour "p" soit définie.

Pour éviter cela, considérez le code tel que:

    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
        }

Répéter. Xcode ne vous imposera pas l'initialisation p dans la classe conforme. Il est essentiel que vous initialisiez p, disons dans viewDidLoad de la classe conforme.

Rendre le code plus simple ...

Vous souhaiterez peut-être utiliser ces deux fonctions globales:

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

Notez qu'ils ne font rien, à part sauver la saisie et rendre le code plus lisible. (Ils sont essentiellement des macros ou des fonctions inline.)

Votre code devient alors:

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

(Dans l'exemple de _aoGet, P est initalisable: au lieu de P (), vous pouvez utiliser "", 0 ou toute valeur par défaut.)



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow