Property Wrappers to mechanizm pozwalający na enkapsulację logiki pracy z właściwościami (np. walidacja, zmiany lub przechowywanie w określony sposób) i ponowne wykorzystanie jej dla różnych właściwości za pomocą adnotacji w kodzie. Pomagają one pozbyć się powtarzającego się kodu i zwiększyć czytelność.
Property Wrapper to struktura, klasa lub enum, które implementują protokół property wrapper za pomocą adnotacji @propertyWrapper oraz obowiązkowej właściwości wrappedValue.
Ograniczenia i niuanse:
Przykład: Napiszemy property wrapper dla automatycznego ograniczenia zakresu wartości (Clamped).
@propertyWrapper struct Clamped<Value: Comparable> { var value: Value let range: ClosedRange<Value> var wrappedValue: Value { get { value } set { value = min(max(newValue, range.lowerBound), range.upperBound) } } init(wrappedValue initialValue: Value, _ range: ClosedRange<Value>) { self.range = range self.value = min(max(initialValue, range.lowerBound), range.upperBound) } } struct Person { @Clamped(0...120) var age: Int = 25 } var p = Person() p.age = 200 // Teraz p.age = 120 p.age = -10 // Teraz p.age = 0
Jaki sposób dostępu do oryginalnego obiektu property wrappera jest dostępny poza strukturą/klasą, w której został zastosowany?
Odpowiedź: Poprzez nazwę właściwości z podkreślnikiem (_). Na przykład, jeśli właściwość nazywa się age, to obiekt property wrappera można uzyskać jako _age:
var p = Person() let wrapper = p._age // to jest typ Clamped<Int>
Historia
W projekcie do przechowywania UserDefaults zaimplementowano niestandardowy property wrapper, który działał z typami prostymi. Przy jego użyciu dla typów referencyjnych (class) wystąpił niespodziewany wyciek pamięci — property wrapper utrzymywał silne odniesienie do obiektu, co prowadziło do silnej pętli i wycieku danych. Błąd został naprawiony przez przejście na słabe/nieposiadające referencje wewnątrz opakowania.
Historia
W projekcie próbowano stosować property wrapper do computed properties, ale kompilator zgłaszał błąd: property wrapper może być stosowany tylko z stored property. Ten fakt umknął, co opóźniło rozwój modułu o 2 dni.
Historia
Podczas tworzenia struktury opakowującej zapomniano o zaimplementowaniu poprawnej składni inicjalizacji przez init(wrappedValue:...). W rezultacie nie można było ustawiać wartości domyślnych przez property wrapper, co ujawniło się dopiero po integracji opakowania w dużej liczbie modeli. Konieczne było przemyślenie architektury.