Szukaj…


Właściwość w rozszerzeniu protokołu uzyskana przy użyciu powiązanego obiektu.

W Swift rozszerzenia protokołu nie mogą mieć prawdziwych właściwości.

Jednak w praktyce można zastosować technikę „obiektu skojarzonego”. Wynik jest prawie dokładnie jak „prawdziwa” właściwość.

Oto dokładna technika dodawania „powiązanego obiektu” do rozszerzenia protokołu:

Zasadniczo używasz wywołań celu-c „objc_getAssociatedObject” i _set.

Podstawowe połączenia to:

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

Oto pełny przykład. Dwa krytyczne punkty to:

  1. W protokole należy użyć „: class”, aby uniknąć problemu mutacji.

  2. W rozszerzeniu należy użyć „where Self: UIViewController” (lub innej odpowiedniej klasy), aby podać typ potwierdzający.

Na przykład przykładowa właściwość „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
}

W każdej klasie zgodnej dodałeś teraz „właściwość” p:

Możesz użyć „p”, tak jak zwykłej właściwości w klasie zgodnej. Przykład:

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

Uwaga. MUSISZ zainicjować pseudo-właściwość.

Xcode nie wymusi inicjalizacji „p” w klasie zgodnej.

Konieczne jest zainicjowanie „p”, być może w widoku viewDidLoad klasy potwierdzającej.

Warto pamiętać, że p to tak naprawdę tylko wyliczona właściwość . p to właściwie tylko dwie funkcje z cukrem syntaktycznym. Nigdzie nie ma p „zmiennej”: kompilator nie „przypisuje żadnej pamięci dla p” w żadnym sensie. Z tego powodu nie ma sensu oczekiwać, że Xcode wymusi „inicjalizację p”.

Rzeczywiście, aby mówić dokładniej, musisz pamiętać, aby „użyć p po raz pierwszy, tak jakbyś go inicjował”. (Ponownie, to bardzo prawdopodobne, że będzie w kodzie viewDidLoad.)

Jeśli chodzi o gettera jako takiego.

Zauważ, że zawiesi się, jeśli getter zostanie wywołany przed ustawieniem wartości „p”.

Aby tego uniknąć, rozważ kod taki jak:

    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
        }

Powtarzać. Xcode nie wymusi inicjalizacji pw klasie zgodnej. Konieczne jest zainicjowanie p, powiedzmy in viewDidLoad klasy zgodnej.

Uproszczenie kodu ...

Możesz skorzystać z tych dwóch globalnych funkcji:

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

Zauważ, że nie robią one nic poza zapisywaniem i poprawiają czytelność kodu. (Są to zasadniczo makra lub funkcje wbudowane).

Twój kod staje się wtedy:

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

(W przykładzie w _aoGet można zainicjować P: zamiast P () można użyć „”, 0 lub dowolnej wartości domyślnej).



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow