Posts tagged: programming

LightWeight View Controller

Ho trovato un interessante articolo che volevo condividere con voi, in cui un gruppo di gente molto competente suggerisce una pratica che consente di scrivere un ViewController lightweight nei progetti iOS. I ViewController sono essenziali in ogni progetto iOS e consentono di gestire le interazioni tra le view e i data model in un’applicazione, come descritto dal pattern MVC. Spesso queste classi contengono molto codice, alle volte più di quanto, in effetti, è necessario. L’idea di base suggerita è quella di separare il codice, lì dove possibile, di gestione dei protocolli ai quali il view controller aderisce. L’esempio seguente è basato su un ViewController derivato dalla classe UITableViewController. Ho cercato di personalizzare l’esempio descritto rendendolo ancora più semplice in modo tale da aumentarne l’efficacia.

#pragma mark UITableViewDataSource
 
- (id)itemAtIndexPath:(NSIndexPath *)indexPath {
    return self.items[(NSUInteger) indexPath.row];
}
 
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.items.count;
}
 
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:self.cellIdentifier
                                                            forIndexPath:indexPath];
    id item = [self itemAtIndexPath:indexPath];
    // Your configuration cell code here
    return cell;
}

Il codice precedente fa riferimento ai metodi del protocollo implementati dal nostro UIViewController per gestire la visualizzazione delle celle di una tabella. Tipicamente questi metodi sono riportati all’interno del ViewController, anche se in realtà hanno a che fare con il datasource (array->NSArray) che contiene i dati della tabella. Proviamo a spostare il codice all’interno della classe che si occupa della gestione degli array:

@interface ArrayDataSource : NSObject 
// your declaration here
@end
 
@implementation ArrayDataSource
 
#pragma mark UITableViewDataSource
 
- (id)itemAtIndexPath:(NSIndexPath *)indexPath {
    return self.items[(NSUInteger) indexPath.row];
}
 
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.items.count;
}
 
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:self.cellIdentifier
                                                            forIndexPath:indexPath];
    id item = [self itemAtIndexPath:indexPath];
    // Your configuration cell code here
    return cell;
}
@end

Bene, ora il nostro controller comincia a diventare più snello. Creiamo quindi un’istanza del nostro nuovo oggetto, a cui passare l’insieme dei dati che popoleranno la tabella, assieme al codice che si occuperà di configurare il contenuto di ciascuna cella.

- (void)setupTableView {
    TableViewCellConfigureBlock configureCell = ^(TableCell *cell, DataSourceItem *content) {
        self.big.text = content.bigDescription;
        self.small.text = content.smallDescription;
    };
 
    NSMutableArray *ds = [[StoreManager sharedManager] readFromSource];
    self.dataSource = [[ArrayDataSource alloc] initWithItems:ds
                                              cellIdentifier:CellIdentifier
                                          configureCellBlock:configureCell];
 
    self.tableView.dataSource = self.dataSource;
    [self.tableView registerClass:[TableCell class] forCellReuseIdentifier:CellIdentifier];
}

Osserviamo che è possibile riutilizzare il metodo setupTableView, ogni qual volta si desidera visualizzare un array all’interno di una tabella senza preoccuparsi di dover mappare un elemento dell’array (atIndex(i)) verso un elemento della tabella (atIndexPath(i)). Inoltre questo tipo di approccio è riutilizzabile nel contesto della UICollectionView, componente simile alle UITableView.
Nell’esempio di sopra la tabella viene popolata dalla classe StoreManager la quale è volutamente esterna al ViewController in quanto designata al recupero di tali informazioni. Questa ulteriore separazione aumenta la leggerezza del nostro ViewController e inoltre ci consente di poter riutilizzare il codice qualora ad esempio, la nostra sorgente dati non sia un array ma una sorgente core data.

Un altro accorgimento da prendere è quello di separare il codice relativo alla gestione delle view all’interno di classi appositamente designate, piuttosto che implementarlo all’interno del ViewController. Nel nostro esempio, il layout di ogni singola cella è demandato alla classe TableCell la quale al suo interno realizza l’inizializzazione del contenuto di ciascuna cella e la corretta visualizzazione. La Category sulla classe TableCell ci consente di disaccoppiare la logica di definizione del contenuto della cella rispetto a quella di definizione del layout della cella stessa. Riscrivendo la sola Category possiamo cambiare la logica di visualizzazione senza impattare su tutto il resto.

Gli esempi di sopra costituiscono piccoli accorgimenti che possono aiutare lo sviluppo del nostro codice rendendo più snelle quelle classi che maggiormente vengono utilizzate. In questo caso specifico abbiamo parlato di UITableViewControllor che tipicamente racchiude molto del codice dei protocolli cui aderisce. Il suggerimento finale è di non forzare l’utilizzo di questi pattern, ma di utilizzarli lì dove si rende necessario e dove viene naturale. Potete trovare l’articolo originale su www.objc.io, in cui viene fatta un’analisi anche su altri argomenti. Consigliato.
Special thanks to: iGriever

Potete trovare il progetto di esempio di quanto detto finora su GitHub.

Objective-C 2.0 – Direttive del compilatore

learn-objective-cSono alle prese con lo sviluppo di un’app per iPhone e siccome mi trovo a dover utilizzare, per forza di cose, le direttive messe a disposizione dal linguaggio, ho pensato che può essere utile avere una lista che le elenca e che ne spiega il significato. Ho trovato anche un post in rete molto interessante che in pratica fa la stessa cosa, e quindi ne approfitto per diffondere anche io nel panorama italiano.

Let’s go !

@class

Dichiara una classe senza dover utilizzare l’istruzione import con il relativo file header. Viene utilizzato per le class forward declaration.

@class DevMeClass;

@protocol @required @optional @end

Definisce l’inizio della dichiarazione di un protocol. Opzionalmente può dichiarare altri protocol ai quali è conforme come segue:

@protocol DevMeProtocol

 

  • @required: dichiara i metodi come metodi required.
  • @optional: dichiara i metodi come metodi optional. La classe che implementa il protocollo può decidere se implementare o no il metodo opzionale. Le classi che fanno uso del protocollo devono verificare che il metodo opzionale esista prima di utilizzarlo.
    [object respondsToSelector:@selector(optionalProtocolMethod)];
  • @end: dichiara la fine del protocollo.

@interface @public @package @protected @private @property @end

Definisce l’inizio di una classe o category.

Dichiarazione di una classe.

Le classi in Objective-C devono derivare direttamente o indirettamente dalla classe NSObject. La direttiva @interface per la dichiarazione di una classe può opzionalmente dichiarare che la classe è conforme ad altri protocolli.

@interface DevMeClass : DevMeSuperClass <DMProtocol, anotherDMProtocol> {
  // instance variables can be
@public
@package
@protected
@private
}
 
// property declarations
@property (atomic, readwrite, assign) id aProperty;
 
// public instance and/or class method declarations
@end

Dichiarazione di una category

La direttiva @interface non può aggiungere variabili d’istanza ma può opzionalmente dichiarare la conformità ad altri protocolli.

@interface DevMeClass (CategoryName) <DMProtocol, anotherDMProtocol>
 
// property declarations
@property (atomic, readwrite, assign) id dmProperty;
 
// method declarations
@end

Direttive

  • @public: dichiara la variabile d’istanza pubblica. Le varibili pubbliche possono essere lette o modificate con la notazione punto
    devme->bestBlog = true;
  • @package: dichiara la variabile d’istanza pubblica all’interno del framework che definisce la classe ma privata all’esterno.Funziona sui sistemi a 64 bit, su quelli a 32 bit si comporta esattamente come @public.
  • @protected: dichiara la variabile d’istanza protetta e quindi accessibile solo dalla classe che la definisce e dalle sue eventuali derivate.
  • @private: dichiara la variabile d’istanza privata e quindi accessibile solo dalla classe che la definisce.
  • @property: dichiara una proprietà la quale può essere accessibile con la notazione punto. Può essere eseguita dalle specialio parole chiave che ne definiscono l’esatto comportamento. Possono essere:
    • readwrite (default), readonly). Genera il getter e il setter (readwrite), solo il getter ((readonly).
    • assign (default), retain, copy. Si applica solo alle proprietà per cui è possibile effetuare un cast sul tipo id. assign, assegna il valore passato; retain, invia un release alla variabile d’istanza, invia retain al nuovo oggetto e assegna l’oggetto retained alla variabile d’istanza; copy invia un release alla varibile d’istanza, invia copy al nuovo oggetto e assegna l’oggetto copiato alla variabile d’istanza. Negli ultimi casi si è responsabili della deallocazione dell’oggetto.
    • atomic(default), non atomic. Le proprietà atomic sono thread-safe mentre le non-atomic possono essere accedute simultaneamente. Le non atomic sono pià veloci delle atomic e spesso usate nelle applicazione single thread.
    • weak (default), strong. Sono disponibili se la funzionalità ARC (automatic reference counting) è abilitata. La chiave strong è sinonimo di retain, mentre weak è sinonimo di assign.
  • @end: dichiara la fine della dichiarazione dell’interfaccia.

 

@throw @try @catch @finally

Vengono utilizzate per la gestione delle eccezioni.

@try {
    // code that might throw an exception &hellip; like this one:
    NSException *exception = 
        [NSException exceptionWithName:@"DevMeException"
                                reason:@"Test exception!"
                              userInfo:nil];
    @throw exception;
}
@catch (CustomException *ce) {
    // handling ...
}
@catch (NSException *ne) {
    // generic NSException handling ...
 
    // to simply re-throw the caught exception in a catch block:
    @throw;
}
@finally  {
    // after try or catch
}

@synchronized

Asscirua che il codice presente all’interno del blocco può essere disponibile solo ad un thread alla volta.

-(void) syncMethod:(id)object {
   @synchronized(object) {
      // code that works with locked object 
   }
}

@autoreleasepool

Nelle applicazioni in cui è abilitato ARC (Automatic Reference Counting) è necessario usare @autoreleasepool al posto di NSAutoreleasePool. Non bisognerebbe dichiarare una variabile all’interno di un blocco @autoreleasepool e continuare ad usarla dopo il blocco.

-(void) dmMethod {
    @autoreleasepool {
        // code that creates a large number of temporary objects
    }
}

@encode

Restituisce il carattere stringa della codifica di un tipo.

-(void) encMethod {
    char *enc1 = @encode(int);                 // enc1 = "i"
    char *enc2 = @encode(id);                  // enc2 = "@"
    char *enc3 = @encode(@selector(aMethod));  // enc3 = ":"
 
    // practical example:
    CGRect rect = CGRectMake(0, 0, 100, 100);
    NSValue *v = [NSValue value:&rect withObjCType:@encode(CGRect)];
}

@compatibility_alias

Consente di generare un alias per una classe. Il primo parametro è il nome dell’alias della classe, il quale può anche non esistere. Il secondo parametro è il nome della classe. a cui l’alias fa riferimento.

@compatibility_alias AliasClassName ExistingClassName

@string

Dichiara un oggetto NSString. Per tale oggetto non è necessario gestirne la memoria.

-(void) devmeMethod {
    NSString* str = @"Ciao questo &egrave; il blog di devme.";
    NSUInteger strLength = [@"Hello world!" length];
}

The end

Siamo giunti alla fine….per ora naturalmente. Sentitevi liberi di comunicare eventuali errori delle direttive. Nel frattempo, direttamente dal blog da cui ho tratto ispirazione, trovate la stessa lista in pdf ;) .

Stay tuned!

WordPress Themes