Wrapper di proprietà in Swift

Feb 06 2023
Cos'è "Property Wrapper"? Dalla guida del linguaggio di programmazione Swift, un wrapper di proprietà è definito come segue: un wrapper di proprietà aggiunge un livello di separazione tra il codice che gestisce il modo in cui una proprietà viene archiviata e il codice che definisce una proprietà. Come suggerisce il nome, un wrapper di proprietà è un tipo che avvolge una proprietà nella nuova logica.

Cos'è "Property Wrapper"?

Dalla guida del linguaggio di programmazione Swift , A Property Wrapper è definito come di seguito:

Un wrapper di proprietà aggiunge un livello di separazione tra il codice che gestisce la modalità di archiviazione di una proprietà e il codice che definisce una proprietà.

Come suggerisce il nome, un wrapper di proprietà è un tipo che avvolge una proprietà nella nuova logica. In termini più semplici, un wrapper di proprietà è una caratteristica del linguaggio rapido che consente di definire un tipo personalizzato per implementare il comportamento dal metodo `getter` e `setter` e riutilizzarlo.

È usato per decorare le proprietà nella tua classe, struct o enum. Aiuta a ridurre il codice boilerplate e migliora la leggibilità.

Quando dovresti usare Property Wrapper?

Prendiamo l'esempio seguente di struct Student che restituisce e stampa il nome in maiuscolo.

struct Student {
    var firstName: String
    var lastName: String
}

  1. Osservatori di proprietà
  2. Proprietà calcolate

struct Student {
    var firstName: String {
        didSet {
            firstName = firstName.uppercased()
        }
    }
    var lastName: String {
        didSet {
            lastName = lastName.uppercased()
        }
    }
    init(firstName: String, lastName: String) {
        self.firstName = firstName.uppercased()
        self.lastName = lastName.uppercased()
    }
}

struct Student {
    private var _firstName: String = ""
    var firstName: String {
        set {
            _firstName = newValue.uppercased()
        }
        get {
            return _firstName
        }
    }
    private var _lastName: String = ""
    var lastName: String {
        set {
            _lastName = newValue.uppercased()
        }
        get {
            return _lastName
        }
    }
    init(firstName: String, lastName: String) {
        self.firstName = firstName
        self.lastName = lastName
    }
}

Come creare un wrapper di proprietà personalizzato?

Per definire un wrapper di proprietà, dobbiamo decorare la classe, lo struct o il tipo enum con la parola chiave @PropertyWrapper e quindi dobbiamo implementare una proprietà chiamata wrapperValue in esso.

Common consente di creare un nuovo tipo chiamato osservatore di proprietà AllCaps per restituire sempre il nome in maiuscolo.

Approccio degli osservatori di proprietà:

@propertyWrapper
struct AllCaps {
    var wrappedValue: String {
        didSet {
            wrappedValue = wrappedValue.uppercased()
        }
    }
    init(wrappedValue: String) {
        self.wrappedValue = wrappedValue.uppercased()
    }
}

Sulla dichiarazione di proprietà, dobbiamo decorare la proprietà con la parola chiave @AllCaps che è un osservatore di proprietà. Abbiamo solo bisogno di aggiungere la parola chiave @AllCaps alle proprietà che restituiscono la proprietà in lettere maiuscole.

@AllCaps var name: String = "Steve Jobs"

In questo approccio all'osservatore di proprietà, dobbiamo applicare la logica in init perché gli osservatori didSet non vengono chiamati quando la proprietà viene inizializzata per la prima volta, quindi dobbiamo aggiungere la logica in init .

Quindi useremo un approccio alternativo alla proprietà calcolata nel wrapper della proprietà.

Approccio di proprietà calcolata:

In questo approccio, stiamo applicando la logica solo nel getter invece di applicarla due volte come l'approccio dell'osservatore di proprietà.

Esempio 1:

@propertyWrapper 
struct AllCaps {
    private var name: String
    var wrappedValue: String {
        set {
            name = newValue
        }
        get {
            return name.uppercased()
        }
    }
    init(wrappedValue: String) {
        self.name = wrappedValue
    }
}

// usage

struct Student {
    @AllCaps var firstName: String
    @AllCaps var lastName: String
}

var student = Student(firstName: "Steve", lastName: "Jobs")
print("Hello \(student.firstName) \(student.lastName)")

// output:

Hello STEVE JOBS

Aggiungiamo un'altra proprietà in student chiamata score che è sempre nell'intervallo tra 0 e 100. Quindi creeremo un altro wrapper di proprietà chiamato InRange che consentirà alla proprietà di essere compresa tra 0 e 100.

@propertyWrapper struct InRange {
    private var mark: Int
    var wrappedValue: Int {
        set {
            mark = newValue
        }
        get {
            return max(0, min(mark, 100))
        }
    }
    init(wrappedValue: Int) {
        mark = wrappedValue
    }
}

struct Student {
    @InRange var mark: Int
}

let student1 = Student(mark: 75)
print(student1.mark)

// output:
75

let student2 = Student(mark: 110)
print(student2.mark)

// output:
100

let student3 = Student(mark: -20)
print(student3.mark)

// output:
0

Esistono numerosi punti in cui possiamo utilizzare un wrapper di proprietà per evitare il duplicato come impostazioni predefinite utente, threading, cambio di orientamento, variabili pigre ecc.,

Nel prossimo articolo, discuteremo un'altra proprietà nel Property Wrapper chiamata Valore proiettato.

Grazie per aver letto!

Seguici su Twitter @evangelistsw