Buscar..


Propiedad, en una extensión de protocolo, lograda utilizando objeto asociado.

En Swift, las extensiones de protocolo no pueden tener propiedades verdaderas.

Sin embargo, en la práctica puede utilizar la técnica de "objeto asociado". El resultado es casi exactamente como una propiedad "real".

Aquí está la técnica exacta para agregar un "objeto asociado" a una extensión de protocolo:

Fundamentalmente, usas el objetivo-c "objc_getAssociatedObject" y las llamadas _set.

Las llamadas básicas son:

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

Aquí hay un ejemplo completo. Los dos puntos críticos son:

  1. En el protocolo, debe usar ": class" para evitar el problema de mutación.

  2. En la extensión, debe usar "where Self: UIViewController" (o la clase apropiada) para dar el tipo de confirmación.

Entonces, para una propiedad de ejemplo "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
}

En cualquier clase conforme, ahora ha "agregado" la propiedad "p":

Puede usar "p" tal como usaría cualquier propiedad ordinaria en la clase conforme. Ejemplo:

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

Nota. DEBES inicializar la pseudo-propiedad.

Xcode no aplicará la inicialización de "p" en la clase conforme.

Es esencial que inicialice "p", tal vez en viewDidLoad de la clase de confirmación.

Vale la pena recordar que p es en realidad una propiedad computada . En realidad, p es solo dos funciones, con azúcar sintáctico. No hay p "variable" en ninguna parte: el compilador no "asigna algo de memoria para p" en ningún sentido. Por esta razón, no tiene sentido esperar que Xcode aplique la "inicialización de p".

De hecho, para hablar con mayor precisión, debes recordar "usar p por primera vez, como si lo estuvieras inicializando". (De nuevo, eso es muy probable que esté en su código viewDidLoad).

Respecto al captador como tal.

Tenga en cuenta que se bloqueará si se llama al captador antes de establecer un valor para "p".

Para evitar eso, considere códigos tales como:

    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
        }

Repetir. Xcode no aplicará la inicialización de p en la clase conforme. Es esencial que inicialice p, por ejemplo en viewDidLoad de la clase conforme.

Haciendo el código más simple ...

Es posible que desee utilizar estas dos funciones 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)
}

Tenga en cuenta que no hacen nada, excepto guardar la escritura y hacer que el código sea más legible. (Son esencialmente macros o funciones en línea).

Su código se convierte entonces en:

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

(En el ejemplo de _aoGet, P es initalizable: en lugar de P (), puede usar "", 0 o cualquier valor predeterminado).



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow