Cosa sono e come utilizzare i Singleton

Quando un’applicazione diventa complessa si ha la necessità di avere dei punti di riferimento all’interno del codice che ti permettono di compiere le azioni ripetitive.

Ad esempio scaricare dei file, connettersi o disconnettersi da un servizio, processare un’immagine e tanto altro ancora.

Ci sono vari modi per risolvere questi problemi, ad esempio potremmo scrivere il codice all’interno di AppDelegate ma con il tempo questa classe diventerebbe davvero affollata e di difficile gestione.
Potremmo creare degli oggetti appositi che vengono istanziati quando serve. Però se abbiamo necessità di mantenere quei dati attivi per tutta la vita dell’app non è la scelta migliore.

Ecco quindi che vengono in aiuto i Singleton.

Altro non è che un pattern che ci permette di avere una singola istanza di una classe, viva per tutto il tempo dell’applicazione. Avremo quindi a disposizione tutti i dati all’interno di quest’oggetto in ogni momento.

Vediamo come crearlo e come utilizzarlo.

Aggiungiamo una nuova classe al progetto: Downloader, sottoclasse di NSObject.

Nel file header dichiariamo un metodo di classe (che ricordo inizia con il segno più “+” invece che con il segno meno “-“):

+ (Downloader*)sharedDownloader;

Poi nel file di implementazione aggiungere il metodo vero e proprio:

+ (instancetype)sharedDownloader {
    static Downloader *_sharedDownloader = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _sharedDownloader = [Downloader new];
    });
    
    return _sharedDownloader;
}

Adesso abbiamo il nostro Singleton, unica istanza all’interno della nostra app.

Per utilizzarla basterà quindi richiamare il metodo sharedDownloader e gli eventuali metodi che abbiamo creato, come ad esempio:

- (void)viewDidLoad {
	[super viewDidLoad];
	
	[[Downloader sharedDownloader] doSomeStuff];
}

Questa è una classe a tutti gli effetti, quindi potete aggiungere proprietà, metodi, delegate e quanto altro vi serve per la vostra applicazione.

Proprio per la sua natura, l’oggetto creato non verrà mai deallocato dalla memoria finché l’app non sarà terminata. Ricordatevi quindi di usare sempre il metodo di classe per utilizzarla e non crearne di nuovi.
Inoltre state attenti a non mantenere dati troppo onerosi di memoria altrimenti rischiate di sovraccaricare l’app.

Come eseguire codice in background o in un secondo thread

Quando il device esegue del nostro codice viene fatto all’interno del processo chiamato MainLoop e che viene eseguito contemporaneamente a molte altre cose, una di queste è l’aggiornamento dell’interfaccia grafica.
Per questo quando eseguite del codice pesante, la UI si blocca.

Per evitarlo e fare in modo che l’app non vada neanche in crash dobbiamo eseguire questo codice in background, cioè in un processo che viene eseguito in un altro thread.

E’ importante specificare da subito che non è consentito di eseguire codice sugli elementi dell’interfaccia grafica in un processo diverso da MainLoop. Se ci provate l’app andrà in crash.

Il metodo più veloce e semplice per eseguire del codice in background (ne esistono anche altri ma per adesso vediamo questo) è utilizzare Grand Central Dispatch (GCD), tecnologia sviluppata da Apple per ottimizzare l’esecuzione del codice.

Assumiamo di avere un array di molti elementi e di dover eseguire del codice molto pensante su ogni elemento. Solitamente scriveremo:

- (void)viewDidLoad {
	[super viewDidLoad];
	NSArray* objects = [NSArray new];
	[objects makeObjectsPerformSelector:@selector(doHeavyCalculations)];

	NSLog(@"Code executed");
}

Il metodo -makeObjectsPerformSelector: di NSArray permette di eseguire un selettore su tutti gli oggetti al suo interno. Ovviamente questi oggetti devono obbligatoriamente rispondere a quel metodo.

In questo modo però, se l’esecuzione del codice è molto pensante, vi ritroverete con l’applicazione che rimane bloccata finché non ha finito di eseguire tutto.

Con GCD invece possiamo fare:

- (void)viewDidLoad {
	[super viewDidLoad];
	
	NSArray* objects = [NSArray new];
	
	1.
	dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
		
		2.
		[objects makeObjectsPerformSelector:@selector(doHeavyCalculations)];
		
		dispatch_async(dispatch_get_main_queue(), ^{
			
			NSLog(@"Code executed");
		
		});
	});
}

1.

Questa parte verrà eseguita in background. Quando avrà finito passerà avanti.

2.
Questa parte di codice viene eseguita nel MainLoop solo dopo aver finito la parte precedente.

E’ importante ricordarsi che ogni blocco eseguito in background deve avere un blocco che fa eseguire del codice nel MainLoop alla fine del processo:

dispatch_async(dispatch_get_main_queue(), ^{}.

Adesso potrete finalmente avere l’app veloce e snella!

Come creare e utilizzare i blocchi

Con l’ultima versione di Objective-C e iOS7 Apple ha aggiunto la possibilità di utilizzare i famosi “blocks”, in italiano Blocchi.

Questo tipo di oggetto contiene dentro di se una porzione di codice che può quindi essere eseguita o trasportata tra altri oggetti, passata come argomento dei metodi ed altro.
In realtà è qualcosa di un po’ più complesso ma per iniziare può andare bene.

La sintassi per la dichiarazione e l’utilizzo dei blocks è un po’ particolare tanto che hanno creato questo simpaticissimo sito per ricordarcela, vediamo quindi come si dichiara e si implementa un blocco:

returnType (^blockName)(parameterTypes) = ^returnType(parameters) { };

Sì, è molto particolare.

Adesso facciamo un esempio pratico di dichiarazione ed utilizzo:

- (void)viewDidLoad {
	[super viewDidLoad];
	
	NSInteger (^blockTest)(NSInteger, NSInteger) = ^NSInteger(NSInteger numberOne, NSInteger numberTwo){
		
		NSInteger result = numberOne + numberTwo;
		return result;
		
	};
}

Abbiamo creato un blocco chiamato “blockTest” che ritorna un NSInteger e vuole come parametri due NSInteger.

Per utilizzarlo possiamo ci basta scrivere quindi:

NSInteger result = blockTest(2, 3);

Possiamo passare anche il blocco come parametro di un metodo:

- (NSInteger)testBlock:(NSInteger (^)(NSInteger, NSInteger))block
{
	NSInteger numberOne = 2;
	NSInteger numberTwo = 3;
	
	return block(numberOne, numberTwo);
}

UIKit utilizza i blocchi ormai per moltissimi scopi, ad esempio UIView li utilizza per eseguire le animazioni.
Ci viene molto utile  quando vogliamo eseguire una porzione di codice prima di un’altra e quindi solo alla fine far eseguire del codice specifico (i famosi completion blocks).

Facciamo un altro esempio. Assumiamo di voler scaricare un file e solo dopo averlo scaricato, volerlo aprire.
Creiamo quindi un metodo che scarica il file da un URL e un blocco che accetta come parametro una stringa, il path finale del file scaricato:

- (void)downloadFileAtURL:(NSURL*)url withCompletion:(void(^)(NSString* filePath))completion
{
	//	codice per scaricare il file
	
	NSString* finalFilePath = @"...";
	
	if (completion)
	{
		completion(finalFilePath);
	}
}

Quindi, il file viene scaricato e poi, quando finito viene passato il path finale al completion block.

Quando andiamo ad utilizzare questo metodo scriveremo semplicemente:

[self downloadFileAtURL:[NSURL URLWithString:@"http://www.example.com/file.zip"] withCompletion:^(NSString *filePath) {
		
	NSLog(@"file downloaded at %@", filePath);
		
}];

Le possibilità ovviamente sono infinite, il limite è la nostra fantasia e alle nostre necessità.

Curiosità

Quando passate un blocco ad un metodo ricordatevi di controllare che il blocco esista, altrimenti se passate “nil” invece che un blocco esistente l’app andrà in crash.

Come usare le Category, cosa sono e a cosa servono in Objective-C

In poche parole, le Category sono delle estensioni di Classi già esistenti.

A differenze di una sottoclasse, una category applica il suo comportamento a tutte le classi. Quindi è molto potente e pure molto pericoloso.

Però se ben sfruttate ci possono aiutare a velocizzare il lavoro e migliorare il nostro progetto.

Notate bene che, a differenza di una sottoclasse, con le category non si possono scrivere dati dentro gli oggetti, quindi si può manipolare solo i dati conosciuti. Ad esempio non possiamo aggiungere delle ivar o delle variabili, ma possiamo calcolare o modificare i loro valori dove possibile.

Proviamo con qualcosa di semplice, il limite sta nella vostra fantasia.. e nelle vostre esigenze.

Mettiamo che abbiamo bisogno di sapere in modo più pratico la posizione o la dimensione di una View.
Possiamo ovviamente avere questi dati in questo modo:

CGSize size = view.frame.size;
CGPoint origin = view.frame.origin;

Ma sarebbe ancora più comodo se potessimo fare:

CGSize size = view.size;
CGPoint origin = view.origin;

Ecco che vengono in aiuto le Category.

Creiamo una classe di tipo Category tramite XCode.
Come nome mettiamo “Sizes” e come Classe di appartenenza mettiamo “UIView”. Avremo quindi “UIView+Sizes.h” e “UIView+Sizes+m”.

Nell’header inseriamo due metodi:

@interface UIView (Sizes)

- (CGSize)size;
- (CGPoint)origin;

@end

Nel file di implementazione invece inseriamo:

@implementation UIView (Sizes)

- (CGSize)size {
return self.frame.size;
}

- (CGPoint)origin {
return self.frame.origin;
}

@end

Pronti. Adesso possiamo importare questa category e tutti i nostri oggetti UIView potranno usufruire di questi due nuovi metodi.

Ovviamente questo è un esempio semplicistico, ma pensate alle sue potenzialità!

Curiosità

Esistono in realtà dei metodi per scrivere all’interno degli oggetti tramite le Category, ma ne parleremo più avanti.

Come creare un protocollo delegate in Objective-C

Abbiamo parlato di come notificare un oggetto nell’articolo Come notificare messaggi con NSNotificationCenter, ma esiste un altro metodo per scambiare messaggi tra gli oggetti.
Si tratta di utilizzare il Delegation Pattern.

Questo pattern è molto utilizzato in Objective-C ed anche dalle classi di Cocoa, molto probabilmente ne avrete fatto uso con UITableView dove viene usato per il delegate e il datasource.

Utilizzarlo è semplice:

1.
@protocol DelegationSampleDelegate;

2.
@interface DelegationSample : NSObject

@property (nonatomic, weak) id  delegate;

@end

3.
@protocol DelegationSampleDelegate 

- (void)doThis:(DelegationSample*)delegationSample;

@end

Analizziamolo:
1. Dichiaro il protocollo così da avere un riferimento in seguito nell’header.

2. Nell’implementazione della classe è necessario avere una proprietà chiamata solitamente “delegate” di tipo id che risponde al protocollo appena creato e che sia “weak”, cioè che non riterrà l’oggetto passato.

3. Questa è la parte interessante. Qui creiamo il protocollo dichiarando i metodi che potremmo utilizzare in seguito.

E’ buona prassi passare anche l’oggetto che invia il messaggio, in questo modo avremo un riferimento chiaro di chi lo ha inviato.
Per esempio protemmo avere vari oggetti dello stesso tipo o vari oggetti che usano il medesimo protocollo e vogliamo sapere chi sta chiamando quel metodo.

Vediamo come utilizzarlo.

Assumiamo di avere un ViewController, dobbiamo far sì che sia conforme al nostro nuovo protocollo così da potersi registrare come delegate:

#import "DelegationSample.h"

@interface ViewController : UIViewController 

@end

Poi nel file di implementazione possiamo creare l’oggetto e registrarci come delegate.

- (void)viewDidLoad {
	[super viewDidLoad];

	DelegationSample* sample = [DelegationSample new];
	sample.delegate = self;
}

A questo punto possiamo utilizzare il metodo di delegate:

- (void)doThis:(DelegationSample*)delegationSample {

	NSLog(@"DelegationSample %@", delegationSample);

}

Per chiamare il metodo di delegate dall’oggetto DelegationSample invece usiamo:

- (void)foo {
	[self.delegate doThis:self];
}

Curiosità:

@protocol ha anche un’instruzione chiamata @optional che ci consente di marcare come opzionali determinati metodi:

@protocol DelegationSampleDelegate 

@optional
- (void)doThis:(DelegationSample*)delegationSample;

@end

In questo caso il metodo è opzionale, però attenzione! Dovete controllare che il delegate risponda a questo selettore prima di invocarlo:

- (void)foo {
	if ([self respondsToSelector:@selector(doThis:)]) {
		[self.delegate doThis:self];
	}
}