swift

I metodi sono le funzioni definite all'interno di classi, strutture ed enumerazioni.

class Contatore {

    var conta = 0

    

    func incrementa() {

        conta += 1

    }

    

    func incrementa(di valore: Int) {

        conta += valore

    }

    

    func azzera() {

        conta = 0

    }

}

let contatore = Contatore()

contatore.incrementa()

contatore.incrementa(di: 5)

print(contatore.conta) //Stampa 6

La classe Contatore ha tre metodi. La cosa interessante è che ci sono due metodi con lo stesso nome, il fatto che abbiano dei parametri differenti (il primo non prende parametri, il secondo un valore intero) li rende differenti.

Il metodo incrementa(di valore: Int) ha come nome del parametro da passare "di valore" e bisogna notare che all'interno della funzione si utilizza "valore" per riferirsi al parametro passato, mentre quando la funzione viene istanziata si usa "di" per passare il parametro. Potrebbe sembrare confuso, in realtà scegliendo opportunamente i nomi dei parametri si ottiene un codice facilmente leggibile. Il manuale Swift dedica un capitolo (Function Argument Labels and Parameter Names) proprio per insegnare a nominare funzioni e parametri in modo conveniente.

La classe Contatore modifica attraverso i propri metodi la proprietà conta. Se volessimo fare lo stesso con strutture o enumerazioni ci scontreremmo con l'affermazione che strutture ed enumerazioni sono tipi valore, una volta creata l'istanza il tipo viene copiato nell'istanza e l'eventuale metodo non è più in grado di modificare il valore della proprietà. Per questo motivo è necessario usare una sintassi differente in modo da avvertire il compilatore di gestire in modo particolare le copie dei valori.

struct Contatore {

    var conta = 0

    

    mutating func incrementa() {

        conta += 1

    }

    

    mutating func incrementa(di valore: Int) {

        conta += valore

    }

    

    mutating func azzera() {

        conta = 0

    }

}

var contatore = Contatore()

contatore.incrementa()

contatore.incrementa(di: 5)

print(contatore.conta) //Stampa 6

Questo è lo stesso codice realizzato in precedenza con la classe. E' da notare la presenza della parola mutating davanti alle funzioni che cambiano il valore della propria proprietà, e l'istanza contatore che da costante è diventata variabile.

La struttura che segue è un esempio per mostrare l'utilizzo dei metodi di tipo (type methods). Si suppone di voler memorizzare il livello raggiunto da un giocatore. I giocatori potrebbero essere diversi e condividere alcuni dati delle giocate. In particolare il gioco sblocca nuovi livelli solo se almeno uno dei giocatori ha raggiunto il livello per poterlo fare. Questa condivisione tra vari giocatori ovvero tra varie istanze della struttura comporta la necessità di specificare alcune proprietà ed alcuni metodi come statici.  

struct SegnaLivello {

    static var livelloSbloccatoMassimo = 1

    var livelloCorrente = 1

    

    static func sblocca(_ livello: Int) {

        if livello > livelloSbloccatoMassimo {

            livelloSbloccatoMassimo = livello

        }

    }

    

    static func sbloccato(_ livello: Int) -> Bool {

        return livello <= livelloSbloccatoMassimo

    }

    

    @discardableResult

    

    mutating func avanza(al livello: Int) -> Bool {

        if SegnaLivello.sbloccato(livello) {

            livelloCorrente = livello

            return true

        }

        else {

            return false

        }

    }

}

 

I type methods sono riconoscibili in quanto sono le funzioni definite con la parola static. Questi metodi accedono o modificano il valore di proprietà di tipo (statiche) di conseguenza devono essere a loro volta static. Il motivo è semplice da comprendere, i metodi non statici risultano utilizzabili da istanze della struttura, ma come abbiamo detto in precedenza (nel capitolo II delle proprietà) le proprietà statiche non devono poter essere accessibili dalle istanze: il metodo risulterebbe un modo per oltrepassare questo vincolo.

La struttura ha livelloSbloccatoMassimo come proprietà di tipo, è un valore statico che viene condiviso tra più istanze della struttura, livelloCorrente è invece riferito ad ogni istanza (giocatore).

La funzione sblocca non fa altro che aggiornare il valore della proprietà livelloSbloccatoMassimo con il nuovo livello. La funzione sbloccato restituisce un valore booleano che indica se il livello passato come parametro è sbloccato oppure no; questo avviene comparando il livello passato con livelloSbloccatoMassimo, la comparazione restituisce true o false. In queste due funzioni è da notare che la prima etichetta del parametro è il carattere '_' che significa che non è necessario specificare il nome del parametro durante la chiamata (lo vediamo nel codice successivo).

La scritta che comincia con il carattere '@' indica un attributo del compilatore. In pratica si dice al compilatore di non emettere warining nel caso non si utilizzi il risultato restituito dalla funzione che segue.

La funzione avanza aggiorna il livello corrente del giocatore se il livello è stato sbloccato.

Nella prossima classe vedremo l'utilizzo della struttura appena creata:

class Giocatore {

    var livello = SegnaLivello()

    let nomeGiocatore : String

    

    init(nome: String) {

        nomeGiocatore = nome

    }

    

    func completa(livello: Int) {

        SegnaLivello.sblocca(livello + 1)

        self.livello.avanza(al: livello + 1)

    }

}

var giocatore = Giocatore(nome: "Paolo")

giocatore.completa(livello: 1)

print("Il livello raggiunto è: \(SegnaLivello.livelloSbloccatoMassimo)")

//Stampa: Il livello raggiunto è: 2

var giocatore2 = Giocatore(nome: "Carlo")

if giocatore2.livello.avanza(al: 6) {

    print("Ora sono al livello 6")

}

else {

    print("Livello ancora bloccato") //Viene stampata questa

}

Nella figura che segue tento di rappresentare graficamente quanto scritto in codice sopra. Viene definita la classe Giocatore che ha due proprietà: nomeGiocatore e livello.

Di questa classe vengono create due istanze denominate giocatore e giocatore2, durante l'inizializzazione viene attribuito un nome ad ogni giocatore. La proprietà livello di ogni giocatore è una struttura di tipo SegnaLivello che racchiude i dati relativi al livello del giocatore. La particolarità è che le due strutture condividono livelloSbloccatoMassimo che mette in relazione i due giocatori in quanto, come mostrato nel codice sopra esposto, il secondo giocatore non può giocare un livello che non è ancora stato sbloccato nè da lui nè dall'altro giocatore. Questa "comunione dei beni" è resa possibile dalle variabili statiche (type properties) della struttura.

Il metodo completa della classe Giocatore merita un chiarimento. Questa funzione viene invocata dal giocatore Paolo per completare il livello corrente ed avanzare al successivo. La funzione invoca il metodo statico della struttura SegnaLivello.sblocca(livello + 1) per sbloccare il livello successivo, questo è possibile solo accedendo direttamente al tipo struttura in quanto si agisce sulla variabile condivisa tra le varie istanze, quella definita statica ovvero la type property. Successivamente con la riga self.livello.avanza(al: livello + 1) si va ad agire sul proprio livello ovvero il livello dell'istanza del primo giocatore. Notare che si usa self per distinguere tra livello istanza e livello etichetta del parametro della funzione. 

metodi