Swift Language
Bijbehorende objecten
Zoeken…
Eigenschap, in een protocoluitbreiding, bereikt met behulp van bijbehorend object.
In Swift kunnen protocolextensies geen echte eigenschappen hebben.
In de praktijk kunt u echter de techniek 'gekoppeld object' gebruiken. Het resultaat is bijna precies als een "echte" eigenschap.
Hier is de exacte techniek voor het toevoegen van een "geassocieerd object" aan een protocoluitbreiding:
Fundamenteel gebruikt u de object-c "objc_getAssociatedObject" en _set-aanroepen.
De basisoproepen zijn:
get {
return objc_getAssociatedObject(self, & _Handle) as! YourType
}
set {
objc_setAssociatedObject(self, & _Handle, newValue, .OBJC_ASSOCIATION_RETAIN)
}
Hier is een volledig voorbeeld. De twee kritieke punten zijn:
In het protocol moet u ": class" gebruiken om het mutatieprobleem te voorkomen.
In de extensie moet u "where Self: UIViewController" (of welke geschikte klasse dan ook) gebruiken om het bevestigingstype te geven.
Dus, voor een voorbeeld eigenschap "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 elke conformerende klasse hebt u nu de eigenschap "p" toegevoegd:
U kunt "p" gebruiken net zoals u elke gewone eigenschap in de conformerende klasse zou gebruiken. Voorbeeld:
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
}
}
Notitie. U MOET de pseudo-eigenschap initialiseren.
Xcode dwingt u niet om "p" te initialiseren in de conformerende klasse.
Het is essentieel dat u "p" initialiseert, misschien in viewDidLoad van de bevestigende klasse.
Het is de moeite waard eraan te denken dat p eigenlijk alleen een berekende eigenschap is . p is eigenlijk slechts twee functies, met syntactische suiker. Er is nergens p "variabel": de compiler wijst op geen enkele manier "geheugen toe voor p". Om deze reden is het zinloos om van Xcode te verwachten dat het "initialiseren p" afdwingt.
Om nauwkeuriger te spreken, moet je inderdaad onthouden dat je "voor het eerst p gebruikt, alsof je het initialiseert". (Nogmaals, dat zou zeer waarschijnlijk in uw viewDidLoad-code staan.)
Wat betreft de getter als zodanig.
Merk op dat het crasht als de getter wordt aangeroepen voordat een waarde voor "p" is ingesteld.
Overweeg code zoals:
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
}
Herhalen. Xcode dwingt u niet om p in de conformerende klasse te initialiseren. Het is essentieel dat u p initialiseert, bijvoorbeeld in viewDidLoad van de conformerende klasse.
De code eenvoudiger maken ...
Misschien wilt u deze twee globale functies gebruiken:
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)
}
Merk op dat ze niets doen, behalve het typen en de code leesbaarder maken. (Het zijn in wezen macro's of inline-functies.)
Uw code wordt dan:
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()
}
}
(In het voorbeeld bij _aoGet is P initaliseerbaar: in plaats van P () kunt u "", 0 of een standaardwaarde gebruiken.)