Objective-C: aggiungere stored properties alle categories

Le Categories sono uno strumento fondamentale per chi sviluppa in Objective-C; permettono di estendere una classe, senza effettuare il subclass, aggiungendo nuovi metodi. Il limite è rappresentato dal non poter aggiungere stored properties, così è definito nella documentazione di Apple, ma in realtà esiste un modo grazie alle caratteristiche (spesso sottovalutate) di Objective-C.

Il meccanismo che permette di superare questo limite delle categories è chiamato associated objects.
Come suggerisce il nome, si tratta di valori associati ad un altro oggetto utilizzando una chiave unica per memorizzarli e recuperarli, similmente a quanto accade per i dictionary.

È possibile ottenere e impostare valori tramite associated objects utilizzando le seguenti due funzioni di runtime e verranno automaticamente rilasciate quando l’oggetto con cui sono associate sarà deallocato:

object rappresenta l’oggetto a cui deve essere associato il valore, key è la chiave univoca con cui associare il valore e value è ovviamente il valore da associare.
L’argomento policy rappresenta la modalità con cui il valore viene immagazzinato; si tratta di un enum messo a disposizione da Objective-C con i seguenti valori:

Spiegato il funzionamento delle associated objects, segue un esempio di come poter aggiungere una stored property ad una category e nello specifico aggiungeremo l’attributo tag  ad una UIImage .

File UIImage+Tag.h:

File UIImage+Tag.m:

Selettori e chiavi

Quando si devono definire molte stored property per una category, può essere noioso (e anche non particolarmente elegante dal punto di vista del codice) definire tutta una serie di chiavi per ogni proprietà.
Anche in questo caso Objective-C ci viene incontro e ci permette di ottimizzare e rendere più pulito e leggibile il codice in quanto i selettori in Objective-C (i nomi del metodo) sono in realtà constant pointers; questo significa che sono adatti per essere utilizzati come chiavi per gli oggetti associati. E’ possibile quindi utilizzare il nome del metodo getter della proprietà come chiave. Il file di implementazione diventerebbe quindi il seguente: