• Programmazione Android
  • CORSI ONLINE
  • Web Agency

Logo

Corsi di programmazione web e mobile online
Navigation
  • Home
  • CORSI ONLINE
  • Tutorial Pratici
  • GUIDE COMPLETE
    • Corso completo di C
    • Corso videogame con Cocos2d
    • Programmazione Cocoa Touch
  • Sezioni
    • Libri e manuali
    • Tips & Tricks
    • Risorse utili
    • Strumenti di Sviluppo
    • Materiale OpenSource
    • Framework
    • Guide Teoriche
    • Guide varie
    • Grafica e Design
    • iPad
    • News
    • Video Tutorial
    • Windows Phone
  • Pubblicità
  • About
    • Chi siamo
    • Pubblicazioni
    • Collabora
    • Sostieni devAPP

3. Objective-C: Le basi – parte 2

By Costantino Pistagna | on 21 Luglio 2011 | 2 Comments
Guida rapida alla programmazione Cocoa Touch

Sistema di esecuzione

Objective-C sposta quante più decisioni possibili dalla compilazione al tempo di esecuzione, cercando di eseguire dinamicamente operazioni come la creazione di oggetti e la determinazione di quale metodo invocare. Il nostro linguaggio, quindi, richiede non solo un compilatore ma anche un sistema di esecuzione, per interpretare correttamente il codice compilato. Il sistema di esecuzione dinamica agisce come una sorta di sistema operativo per il linguaggio.


banner-iphoneSmartApps-org

Il tipo id

In Objective-C, gli identificatori di oggetti sono un tipo a parte chiamato id. Ogni oggetto, indipendentemente dalla classe di appartenza e’ di questo tipo. Id puo’ essere utilizzato sia per le istanze che per le classi stesse; esso e’ definito come un puntatore ad una struttura di questo tipo:

typedef struct objc_object {

Class isa;

} *id;

Tutti gli oggetti hanno una variabile isa che indica la classe di appartenenza. Allo stesso modo degli array in C, ogni oggetto e’ identificato dal suo indirizzo (puntatore). La parola chiave nil e’ definita come un oggetto nullo o un id con valore 0. Id, nil e tutti i tipi base di Objective-C sono definiti nel file di header objc/objc.h .

Dynamic Typing

Il tipo id e’ assolutamente non restrittivo: esso non contiene alcuna informazione aggiuntiva. Semplicemente indica che stiamo dialogando con un oggetto. Ovviamente gli oggetti non sono tutti uguali: un rettangolo non avra’ gli stessi metodi e variabili d’istanza di un oggetto che rappresenta un’immagine.
Ad un certo punto della sua esecuzione, ogni programma ha bisogno di trovare informazioni specifiche circa gli oggetti che ha al suo interno – variabili di istanza contenute, metodi che puo’ eseguire e cosi’ via. Dal momento che id non puo’ fornire queste informazioni al compilatore, ogni oggetto deve poterle fornire in qualche altra maniera durante l’esecuzione del codice.

La variabile di istanza isa identifica la classe dell’oggetto. Ogni rettangolo sara’ in grado di dire che esso e’ un oggetto di tipo rettangolo ed ogni cerchio sapra’ di essere un cerchio. Oggetti con lo stesso comportamento (metodi) e con gli stessi dati (variabili di istanza) sono definiti essere membri della stessa classe.

Gli oggetti, quindi, sono tipati durante l’esecuzione. Quando serve, il sistema di esecuzione puo’ trovare la classe corrispondente, semplicemente chiedendolo.

La variabile isa, inoltre permette di ottenere il concetto di introspezione. Il compilatore conserva informazioni circa le definizioni di classe in particolari strutture dati, specifiche per essere usate dal sistema di esecuzione. Quest’ultimo usa la variabile isa per recuperarle quando serve. Utilizzando il sistema di esecuzione dinamica, ad esempio, e’ possibile determinare quando un oggetto implementa un particolare metodo oppure ottenere il nome della superclasse dalla quale si eredita.

Gestione della memoria

In un programma Objective-C e’ importante assicurarsi che gli oggetti siano deallocati correttamente quando non sono piu’ necessari all’esecuzione, per evitare pericolosi sprechi di memoria – leaks. Inoltre, e’ altrettanto importante accertarsi che gli oggetti deallocati non siano piu’ utilizzati.

Objective-C 2.0 offre due sistemi di gestione della memoria:

  • Reference counting: in questo caso saremo noi i responsabili diretti della vita del nostro oggetto
  • Garbage collection: in questo caso passeremo la responsabilita’ circa l’allocazione dei nostri oggetti ad un sistema automatizzato

Nella programmazione Objective-C ed iPhone si fa’ un uso ibrido di queste due tecnologie. In questo modo avremo la liberta’ di decidere se il nostro oggetto deve essere gestito automaticamente o meno.

La gestione della memoria segue due semplici regole che proveremo a riassumere in questo modo:

Regola 1 – Se un oggetto viene creato usando una alloc o una copy, e’ necessario liberare la memoria quando sono state completate le operazioni sull’oggetto.

Regola 2 – Se l’oggetto non viene creato direttamente (regola sopra), non bisogna assolutamente tentare di rilasciare la memoria con release.

Ad esempio, il seguente oggetto e’ stato creato con una alloc. I responsabili, quindi, del rilascio della memoria siamo noi.

TestClass *ptr = [[TestClass alloc] init];

...

[ptr release];

Proviamo a vedere, invece, cosa accade quando la responsabilita’ per l’allocazione dell’oggetto non e’ nostra.

NSString *str;
str = [NSString stringWithFormat:@"Some string here..."];
NSLog(str);

In quest’esempio utilizziamo un metodo di classe – stringWithFormat – per la creazione di una nuova stringa da associare al puntatore str. Il metodo non utilizza direttamente la chiamata alloc; in questo caso non saremo noi a dover gestire il rilascio della memoria.

NSString *str;
// No need to release this object
str = [TestClass firstName];

In questo secondo esempio, utilizziamo un metodo di istanza che ritorna un puntatore ad un oggetto stringa. Se vogliamo trattenere la memoria per quest’oggetto, dovremo farlo in maniera esplicita utilizzando il metodo retain.
Piu’ in generale, se semplicemente ci serve un puntatore all’oggetto stringa sopra, possiamo assegnarlo ad una variabile, senza preoccuparci di rilasciare la memoria.

Nel caso dell’utilizzo del metodo di classe new, valgono le regole di cui al punto 1; saremo responsabili,cioe’, del rilascio della memoria.

MyObject *str;
str = [MyObject new];
...
...
[str release];

Inviare messaggi a nil

In Objective-C e’ perfettamente valido inviare un messaggio a nil: semplicemente non avra’ nessun effetto. Esistono molti esempi nel framework Cocoa che traggono spunto da questa caratteristica. Il valore ritornato inviando un messaggio a nil, potrebbe essere anch’esso valido:

Se il metodo ritorna un oggetto, allora un messaggio inviato a nil ritorna 0. Ad esempio:

Persona *madre = [[unaPersona sposa] madre];

Se non siamo sposati (il messaggio sposa, ritorna nil), allora il messaggio madre sara’ inviato a nil:

[nil madre];

Se il metodo ritorna un qualunque tipo puntatore o un qualunque tipo base, allora un messaggio inviato a nil ritorna 0.

Inizializzazione degli oggetti e NIL

L’ordine degli avvenimenti durante l’inizializzazione di un oggetto e’ di fondamentale importanza per il corretto funzionamento del nostro codice. Ad esempio, proviamo ad osservare il codice seguente:

TestClass *ptr = [TestClass alloc];
[ptr init];

// Do something with the object
if (ptr)
...

Allochiamo un oggetto ed inviamo un messaggio di inizializzazione. Il problema risiede proprio nella temporizzazione di questi eventi. Se il metodo init ritorna nil (ogni buon metodo init dovrebbe ritornare nil se qualcosa fallisce durante l’inizializzazione), il test alla riga 5 verra’ passato con successo: ptr, infatti, non sara’ nil.
Proviamo a confrontare quest’approccio con una versione piu’ precisa:

TestClass *ptr = [[TestClass alloc] init];

// Do something with the object
if (ptr)
...

In questo caso, se il metodo di inizializzazione fallisce, ptr verra’ impostato a nil ed il test alla riga 4 non verra’ passato.

Passaggio di argomenti multipli ad un metodo

Continuiamo a lavorare con la nostra classe Fraction, facendo alcune aggiunte importanti su di essa. Sarebbe interessante poter aggiungere un metodo che configura in maniera atomica numeratore e denominatore. E’ possibile definire metodi che prendono argomenti multipli, semplicemente elencando tutti gli argomenti uno dopo l’altro seguiti da un segno di due punti (:). Tutti i parametri diventeranno parte del nome stesso del metodo (firma).
Ad esempio, il metodo addEntryWithName:andEmail: e’ un metodo che prende due argomenti. Allo stesso modo, il metodo addEntryWithName:andEmail:andPhone: sara’ un metodo che prende tre argomenti.

Ritornando a noi, un metodo che configura contemporanemanete numeratore e denominatore, potrebbe essere scritto cosi’:

[myFraction setTo:1 over:3];

aggiungiamo questo nuovo metodo alla nostra interfaccia:

@interface Fraction: Object {
int numerator;
int denominator;
}
- (void) print;
- (void) setNumerator: (int) n;
- (void) setDenominator: (int) d;
- (void) setTo: (int) n over: (int) d;
@end

ed alla nostra implementazione:

@implementation Fraction;
-(void) print {
printf(" %i / %i ", numerator, denominator);
}

-(void) setNumerator: (int) n {
numerator = n;
}

-(void) setDenominator: (int) d {
denominator = d;
}

-(void) setTo: (int) n over: (int) d {
numerator = n;
denominator = d;
}

@end

Il nuovo metodo prende due argomenti interi e configura le variabili di istanza, interne all’oggetto, in maniera opportuna.

Passaggio di parametri: variabili locali ed oggetti

Continuiamo il nostro lavoro con la classe Fraction. Prima di tutto, scriveremo un metodo che ci permettera’ di sommare una frazione ad un’altra. Chiameremo il nostro metodo add:. Esso prendera’ come argomento una frazione.

-(void) add: (Fraction *)f;

Ricordiamo brevemente il concetto di messaggio. Ogni qualvolta inviamo un messaggio ad un oggetto, eseguiamo un suo metodo di istanza. Nel nostro caso, inviare un messaggio di addizione ad un oggetto, significhera’ invocare il suo metodo add:.
La frazioneA che riceve un messaggio di addizione, riceve contestualmente anche un oggetto – un puntatore – come argomento del messaggio. L’argomento passato e’ un puntatore alla frazioneB da addizionare.

-(void) add: (Fraction *)f {
numerator = (numerator * [f denominator]) + (denominator * [f numerator]);
denominator = denominator * [f denominator];
}

I nomi utilizzati per riferirsi ai parametri passati ad un metodo, sono a tutti gli effetti delle variabili locali che possiedono una copia del valore passato e vengono create e distrutte nell’arco di vita del nostro metodo. Non risultera’ possibile, quindi, modificare il valore passato come parametro, dal momento che dialoghiamo con una semplice copia della variabile originale.

Nel caso dell’esempio sopra, quando passiamo un oggetto come parametro le regole sono leggermente diverse. Se guardiamo attentamente l’esempio, infatti, ci accorgeremo che il nostro oggetto frazione viene passato sottoforma di puntatore (asterisco prima del nome). Questo significa che il metodo che ricevera’ questo messaggio – e quindi il parametro con il puntatore alla frazione argomento – dialoghera’ direttamente con l’oggetto. In questo caso, ogni variazione alle variabili di istanza dell’oggetto parametro, risultera’ valida anche alla fine del ciclo di vita del nostro metodo.

La keyword self

La nostra classe Fraction adesso e’ in grado di sommare due frazioni tra loro. Se osserviamo il
codice, ci accorgeremo del fatto che la somma non tiene conto delle riduzioni tra numeratore e denominatore.
Se sommiamo alla frazione avente valore 1/2, la frazione con valore 1/4, otteniamo come risultato 6/8 e non 3/4. Il nostro metodo di somma non tiene conto della riduzione. Per risolvere il problema, possiamo procedere in due modi; possiamo modificare il nostro metodo di somma per effettuare un’ulteriore operazione di riduzione, oppure possiamo creare un nuovo metodo chiamato riduzione ed usarlo dove necessario:

- (void) reduce {
int u = numerator;
int v = denominator;
int temp;

while(v != 0) {
temp = u % v;
u = v;
v = temp;
}

numerator /= u;
denominator /= u;
}

Il metodo effettua la riduzione di numeratore e denominatore in rapporto al modulo tra l’uno e l’altro. Aggiungiamo la dichiarazione all’interfaccia:

- (void) reduce;

Adesso siamo pronti per utilizzarlo dove necessario. Dal momento che si tratta di un normale metodo di istanza, puo’ essere richiamato da qualunque istanza di frazione:

...
...
[aFraction add:bFraction];
[aFraction reduce];
...
...

Questa soluzione, pero’, implica che dobbiamo cambiare tutto il codice in cui occorre l’uso della funzione di somma. Un’alternativa sarebbe quella di richiamare il metodo di riduzione, direttamente all’interno del metodo di somma, modificandone quindi il funzionamento.

Per fare questo, e’ necessario introdurre la nozione di self. Ogni istanza di classe, puo’ utilizzare questa parola chiave per riferirsi a se stessa come istanza. Essendo un puntatore ad istanza completamente valido, nulla ci vieta di lanciare un messaggio su di esso. Con queste premesse, potremo riscrivere il metodo di somma come segue:

-(void) add: (Fraction *)f {
numerator = (numerator * [f denominator]) + (denominator * [f numerator]);
denominator = denominator * [f denominator];

[self reduce]; //launch a reduce message to myself
}

Allocazione e ritorno di oggetti all’interno dei metodi

Se lanciamo il codice con le modifiche descritte, noteremo che il metodo add: cambia il valore dell’oggetto che riceve questo messaggio. Vediamo come creare un metodo che crea una nuova frazione con il risultato dell’operazione di somma senza alterare i valori del mittente. Per fare questo, avremo bisogno di creare una nuova frazione risultato e restituirla al mittente del messaggio.

-(Fraction *)add: (Fraction *)f {
Fraction *result = [[Fraction alloc] init];
int resultNum, resultDenom;

resultNum = (numerator * [f denominator] + (denominator * [f denominator]);
resultDenom = denominator * [f denominator];

[result setTo: resultNum over: resultDenom];
[result reduce];

return result;
}

Il metodo alloca ed inizializza una nuovo oggetto frazione chiamato result. I valori delle variabili locali resultNum e resultDenom sono utilizzate per ospitare il risultato dell’operazione e, successivamente, come parametri per il metodo setTo:over:.
Dopo aver effettuato le operazioni di riduzione, ritorniamo il nuovo oggetto creato al chiamante. Nota che la memoria allocata non verra’ in nessun modo rilasciata; sara’ l’oggetto che riceve il risultato a doversi preoccupare di deallocarla quando non sara’ piu’ utilizzata.

L’oggetto root: Object

Abbiamo gia’ parlato dell’idea di ereditarieta’ quando abbiamo discusso del padre di una classe (lezione 2). Ovviamente, anche una classe padre puo’ avere un padre. La classe che non ha nessun padre, si trova alla radice dell’albero genealogico ed e’ conosciuta con il nome di root. In Objective-C, e’ possibile definire una propria classe radice anche se, normalmente, e’ una cosa poco comune. Al contrario, quello che si fa’ solitamente e’ trarre vantaggio dalle classi preesistenti.
Tutte le classi definite sino ad ora nei nostri esempi, sono discendenti della classe radice chiamata Object

@interface Fraction: Object
...
...
@end

La classe Fraction e’ anche definita come classe figlia o sottoclasse


Objective-C-Le-basi-parte2-devAPP

Da un punto di vista di terminologia, durante il corso, parleremo di classi, classi figlie e classi padre. Oppure, indifferentemente, parleremo di classi, sottoclassi e superclassi. Entrambe le terminologie sono comunemente utilizzate e dovrebbero diventare di uso comune quando ci riferiamo alla programmazione Objective-C

Ogni qualvolta viene definita una nuova classe (diversa da quella root), vengono ereditate alcune proprieta’ dalla classe padre. Ad esempio, tutte le variabili di istanza ed i metodi di istanza della classe padre sono implicitamente ereditate anche dalle classi figlie. Questo significa che le sottoclassi possono accedere a questi metodi e variabili direttamente, come se fossero parte della dichiarazione di interfaccia della sottoclasse stessa.

Creazione di un’applicazione programmatica







Riferimenti

  • The Objective-C Programming Language: Objects, Classes, and Messaging
  • The Objective-C Programming Language: Defining a Class
  • The Objective-C Programming Language: Allocating and Initializing Objects
  • Memory Management Programming Guide
  • Programming in Objective-C: Capitoli 4, 7 e 8


banner-iphoneSmartApps-org

Share this story:
  • tweet

Tags: dynamic typing objective-cgestione memoria objective-cguida programmazione cocoalezioni objective-cnil objective-cObjective-coggetto object objective-cpassaggio parametri objective-cself objective-ctipo id objective-c

Recent Posts

  • Parte il percorso programmatori iOS in Swift su devACADEMY.it

    20 Dicembre 2017 - 0 Comment
  • Android, crittografare dati velocemente con Encryption

    24 Settembre 2018 - 0 Comment
  • Sql2o, accesso immediato ai database tramite Java

    3 Settembre 2018 - 0 Comment
  • Okio, libreria per ottimizzare l’input/output in Java

    27 Agosto 2018 - 0 Comment

Related Posts

  • Uno sguardo al runtime Objective-c

    10 Settembre 2013 - 0 Comment
  • Andrea Picchi: Programmare con Objective-C 2.0 per iOS e OS X

    27 Febbraio 2013 - 10 Comments
  • T#107 – Le referenze associative: Aggiungiamo le variabili alle Categorie

    2 Aprile 2012 - 3 Comments

Author Description

2 Responses to “3. Objective-C: Le basi – parte 2”

  1. 4 Agosto 2011

    ultrakorne

    “Nella programmazione Objective-C ed iPhone si fa’ un uso ibrido di queste due tecnologie. In questo modo avremo la liberta’ di decidere se il nostro oggetto deve essere gestito automaticamente o meno.”

    è scorretto, in iOS non c’è garbage collection, l’autorelease non è in nessun modo simile a un garbage collector. un oggetto al quale viene inviato un messaggio di autorelease semplicemente si posticipa, in maniera automatica, il release al prossimo “ciclo”.

    da ios 5 avremo ARC ( e nemmeno lui è qualcosa come un garbage collector)

  2. 22 Settembre 2011

    nikus68

    una piccola precisazione. Return non si traduce con ritorna, poiché non è un verbo transitivo (è dialettale). Bisogna usare il verbo restituire. Comunque ottimo articolo per il resto.

Leave a Reply

Your email address will not be published. Required fields are marked *


*
*

Corso online di programmazione android e java

SEZIONI

  • Android
  • Comunicazioni
  • Contest
  • Corsi ed Eventi
  • Corso completo di C
  • Corso programmazione videogiochi
  • Framework
  • Grafica e Design
  • Guida rapida alla programmazione Cocoa Touch
  • Guide Teoriche
  • Guide varie
  • iPad
  • Le nostre applicazioni
  • Libri e manuali
  • Materiale OpenSource
  • News
  • Pillole di C++
  • Progetti completi
  • Risorse utili
  • Strumenti di Sviluppo
  • Swift
  • Tips & Tricks
  • Tutorial Pratici
  • Video Tutorial
  • Windows Phone

Siti Amici

  • Adrirobot
  • Allmobileworld
  • Apple Notizie
  • Apple Tribù
  • Avvocato360
  • Blog informatico 360°
  • bubi devs
  • fotogriPhone
  • GiovaTech
  • iApp-Mac
  • iOS Developer Program
  • iPodMania
  • MelaRumors
  • Meritocracy
  • SoloTablet
  • TecnoUser
  • Privacy & Cookie Policy
©2009-2018 devAPP - All Rights Reserved | Contattaci
devAPP.it è un progetto di DEVAPP S.R.L. - Web & Mobile Agency di Torino
Str. Volpiano, 54 - 10040 Leini (TO) - C.F. e P.IVA 11263180017 - REA TO1199665 - Cap. Soc. € 10.000,00 i.v.

devACADEMY.it

Vuoi imparare a programmare?

Iscriviti e accedi a TUTTI i corsi con un’unica iscrizione.
Oltre 70 corsi e migliaia di videolezioni online e in italiano a tua disposizione.

ISCRIVITI SUBITO