Swift Language
Obiekty powiązane
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:
W protokole należy użyć „: class”, aby uniknąć problemu mutacji.
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).