Swift Language
Associated Objects
Sök…
Egenskap, i en protokollutvidgning, uppnådd med tillhörande objekt.
I Swift kan protokolltillägg inte ha riktiga egenskaper.
I praktiken kan du dock använda tekniken "associerat objekt". Resultatet är nästan exakt som en "riktig" egendom.
Här är den exakta tekniken för att lägga till ett "associerat objekt" till ett protokolltillägg:
I grund och botten använder du objektiv-c "objc_getAssociatedObject" och _set samtal.
De grundläggande samtalen är:
get {
return objc_getAssociatedObject(self, & _Handle) as! YourType
}
set {
objc_setAssociatedObject(self, & _Handle, newValue, .OBJC_ASSOCIATION_RETAIN)
}
Här är ett fullständigt exempel. De två kritiska punkterna är:
I protokollet måste du använda ": class" för att undvika mutationsproblemet.
I tillägget måste du använda "där Själv: UIViewController" (eller vilken klass som helst) för att ge den bekräftande typen.
Så till exempel en egenskap "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
}
I vilken klass som helst, har du nu "lagt till" egenskapen "p":
Du kan använda "p" precis som du skulle använda alla vanliga fastigheter i den överensstämmande klassen. Exempel:
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
}
}
Notera. Du MÅ initiera pseudoegenskapen.
Xcode kommer inte att tvinga dig att initialisera "p" i motsvarande klass.
Det är viktigt att du initierar "p", kanske i viewDidLoad i den bekräftande klassen.
Det är värt att komma ihåg att p faktiskt bara är en beräknad egenskap . p är faktiskt bara två funktioner, med syntaktiskt socker. Det finns ingen p "variabel" någonstans: kompilatorn tilldelar inte något minne för p "på något sätt. Av denna anledning är det meningslöst att förvänta sig att Xcode verkställer "initialisering p".
För att tala mer exakt måste du komma ihåg att "använda p för första gången, som om du initialiserade det". (Återigen skulle det mycket troligt vara i din viewDidLoad-kod.)
När det gäller getter som sådan.
Observera att det kommer att krascha om gettern kallas innan ett värde för "p" ställs in.
För att undvika det, överväg kod som:
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
}
Att upprepa. Xcode kommer inte att tvinga dig att initialisera p i motsvarande klass. Det är viktigt att du initialiserar p, säg i viewDidLoad för den överensstämmande klassen.
Gör koden enklare ...
Du kanske vill använda dessa två globala funktioner:
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)
}
Observera att de inte gör någonting annat än att spara skrivning och göra koden mer läsbar. (De är i huvudsak makro eller inlinefunktioner.)
Din kod blir då:
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()
}
}
(I exemplet på _aoGet är P initaliserbar: istället för P () kan du använda "", 0 eller vilket som helst standardvärde.)