Swiftのプロパティラッパー内から、誰かがクラスのインスタンスを参照したり、ラップされているプロパティを所有しているストライクをしたりできますか? self
を使用しても、明らかに機能せず、super
も機能しません。
self
をプロパティラッパーのinit()
に渡そうとしましたが、self
のConfiguration
がまだ定義されていないため、これも機能しません@propertywrapper
が評価されます。
私の使用例は、多数の設定または構成を管理するクラスです。プロパティが変更された場合は、関係者にsomethingが変更されたことを通知します。実際にどの値を知っている必要はないので、各プロパティにKVO
やPublisher
のようなものを使用する必要はありません。
プロパティラッパーは理想的に見えますが、ラッパーがコールバックできる所有インスタンスへの参照のようなものを渡す方法がわかりません。
参照:
enum PropertyIdentifier {
case backgroundColor
case textColor
}
@propertyWrapper
struct Recorded<T> {
let identifier:PropertyIdentifier
var _value: T
init(_ identifier:PropertyIdentifier, defaultValue: T) {
self.identifier = identifier
self._value = defaultValue
}
var value: T {
get { _value }
set {
_value = newValue
// How to callback to Configuration.propertyWasSet()?
//
// [self/super/...].propertyWasSet(identifier)
}
}
}
struct Configuration {
@Recorded(.backgroundColor, defaultValue:NSColor.white)
var backgroundColor:NSColor
@Recorded(.textColor, defaultValue:NSColor.black)
var textColor:NSColor
func propertyWasSet(_ identifier:PropertyIdentifier) {
// Do something...
}
}
答えはノーです。現在の仕様では不可能です。
似たようなことをしたかった。私が思いつくことができる最高のものは、init(...)
の最後にある関数でリフレクションを使用することでした。少なくともこの方法で、タイプに注釈を付け、init()
に1つの関数呼び出しのみを追加できます。
fileprivate protocol BindableObjectPropertySettable {
var didSet: () -> Void { get set }
}
@propertyDelegate
class BindableObjectProperty<T>: BindableObjectPropertySettable {
var value: T {
didSet {
self.didSet()
}
}
var didSet: () -> Void = { }
init(initialValue: T) {
self.value = initialValue
}
}
extension BindableObject {
// Call this at the end of init() after calling super
func bindProperties(_ didSet: @escaping () -> Void) {
let mirror = Mirror(reflecting: self)
for child in mirror.children {
if var child = child.value as? BindableObjectPropertySettable {
child.didSet = didSet
}
}
}
}
私の実験は次に基づいています: https://github.com/Apple/Swift-evolution/blob/master/proposals/0258-property-wrappers.md#referencing-the-enclosing-self-in-a-wrapper -type
protocol Observer: AnyObject {
func observableValueDidChange<T>(newValue: T)
}
@propertyWrapper
public struct Observable<T: Equatable> {
public var stored: T
weak var observer: Observer?
init(wrappedValue: T, observer: Observer?) {
self.stored = wrappedValue
}
public var wrappedValue: T {
get { return stored }
set {
if newValue != stored {
observer?.observableValueDidChange(newValue: newValue)
}
stored = newValue
}
}
}
class testClass: Observer {
@Observable(observer: nil) var some: Int = 2
func observableValueDidChange<T>(newValue: T) {
print("lol")
}
init(){
_some.observer = self
}
}
let a = testClass()
a.some = 4
a.some = 6
現在、これをそのまま使用することはできません。
ただし、あなたが参照する提案では、これを最新バージョンの将来の方向性として説明しています: https://github.com/Apple/Swift-evolution/blob/master/proposals/0258-property-wrappers.md# reference-the-enclosing-self-in-a-wrapper-type
現時点では、projectedValue
を使用してself
を割り当てることができます。次に、それを使用して、wrappedValue
を設定した後でアクションをトリガーできます。
例として:
import Foundation
@propertyWrapper
class Wrapper {
let name : String
var value = 0
weak var owner : Owner?
init(_ name: String) {
self.name = name
}
var wrappedValue : Int {
get { value }
set {
value = 0
owner?.wrapperDidSet(name: name)
}
}
var projectedValue : Wrapper {
self
}
}
class Owner {
@Wrapper("a") var a : Int
@Wrapper("b") var b : Int
init() {
$a.owner = self
$b.owner = self
}
func wrapperDidSet(name: String) {
print("WrapperDidSet(\(name))")
}
}
var owner = Owner()
owner.a = 4 // Prints: WrapperDidSet(a)