• 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

L#008 – Objective-C (Parte III) Gestiamo la memoria

By IgnazioC | on 3 Dicembre 2009 | 12 Comments
Guide Teoriche

In questo articolo analizzeremo un aspetto della programmazione per iphone che spesso è responsabile di lunghe ore di debug e di frustrazione. Stiamo parlando, ovviamente, della gestione della memoria.
È bene precisare che tutto quello che diremo qui si applica solo agli oggetti nativi dell’ Objective-C mentre per le variabili dichiarate con la sintassi C (int, char, float) restano valide le regole di questo linguaggio.

Una corretta e attenta gestione della memoria sui dispositivi mobili e, soprattutto, su ipod/iphone è di cruciale importanza, viste le ridotte capacità di elaborazione di questi device.
Proprio per le ridotte capacità di elaborazione è stato necessario privare i nostri iphone di una vera e propria Garbage Collection, utilizzando in sua vece un più complesso Autorelease Pool.
Ma quali sono le differenze? La GC monitora costantemente i nostri oggetti, i quali vengono eliminati solo su nostra richiesta o quando non esistono più riferimenti per accedere a quell’oggetto. Questo  implica che se esiste un riferimento ad un oggetto, questo è sicuramente valido.
L’autorelease pool invece è l’esatto contrario, elimina tutti gli oggetti, tranne quelli per i quali abbiamo fatta esplicita richiesta che vengano mantenuti. Il motivo di tale approccio è facilmente intuibile, ed è da ricercare nel fatto che nei device attuali la memoria è un bene prezioso e ci non possiamo permettere che i nostri programmi la sprechino.

L’autorelase viene istanziato nel main delle nostre applicazioni, con questa istruzione:

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

e lo possiamo considerare quindi alla stessa stregua del tristo mietitore pronto ad eliminare tutti gli oggetti ritenuti inutili, compito che svolge analizzando una particolare proprietà di ogni oggetto chiamata retainCount. In particolare vengono eliminati tutti quegli oggetti che hanno un valore di retainCount pari a zero.
Per far funzionare il nostro programma secondo le nostre aspettative dovremo quindi fare in modo che gli oggetti necessari  abbiamo retainCount maggiore o uguale a 1.

Se per qualche ragione siamo interessati a sapere quando l’autorelease elimina i nostri oggetti, possiamo sfruttare il fatto che viene invocato in maniera trasparente il metodo dealloc sull’oggetto da eliminare, quindi eseguendo un opportuno override di questo metodo possiamo ottenere tutte le informazioni di cui abbiamo bisogno.

Quali sono quindi i momenti in cui retainCount varia?

  • Quando una variabile viene inizializzata il suo retainCount viene incrementato di 1.
  • I metodi alloc, copy, allocWithZone incrementano retainCount di 1.
  • I metodi release, autorelease decrementano retainCount di 1. (autorelease lo fa in un momento non deterministico)
  • Se non diversamente specificato il metodo autorelease viene invocato di default.

Vediamo qualche esempio:

TheClass *newObject = [[TheClass alloc] init];

In questo caso il retainCount di newObject sarà 1  per via del metodo alloc. (in realtà riceve anche un +1 per l’inizializzazione e un -1 per l’autorelease implicito)
L’area di memoria assegnata a questo oggetto quindi non potrà essere liberata, neanche quando usciremo dallo scope della variabile newObject. In questo caso avremo perso ogni riferimento a questo oggetto e avremo creato quello che viene chiamato leak.

NSString *myString = @"Hello World";

In questo caso il retainCount di myString sarà pari a zero, perchè +1 lo riceverà per via della inizializzazione e -1 per via dell’ autorelease implicito. Questo significa che la zona di memoria assegnata a myString verrà liberata dall’autoreleasePool nonappena questi lo riterrà opportuno, non prima del termine dell’esecuzione del metodo corrente però!

NSString *myString = [NSString stringWithFormat:@"HELLO WORLD"] autorelease];

Stesso caso del precedente ma con la chiamata esplicita di autorelease.

Facciamo un piccolo esempio per comprendere quando sia indispensabile la far propri questi meccanismi. Dichiariamo in una nostra classe una variabile (variabile di istanza)

NSString *myString

La inizializziamo in un metodo, e vi accediamo da un secondo.

- (void)viewDidLoad {
    myString = [[NSString stringWithFormat:@"ciao"] autorelease];
    [super viewDidLoad];
}
//..altro codice...
- (IBAction)otherMethod:(id)sender{
   NSLog(myString);
}

Questo codice per quanto sembri corretto invece andrà in errore, perchè avendo chiamato il metodo autorelease sull’oggetto myString ne abbiamo portato il retainCount a zero, quindi viene eliminata dall’autorelease pool.

Un caso ancora più significativo avviene quando gli oggetti diventano parametri dei nostri metodi, vediamo questo codice:

//CLASSE 1
- (void)metodo1{
	NSString *saluto = [NSString stringWithFormat:@"Ciao mondo"];
	[classe2 metodo2:saluto]
}
//CLASSE2
NSString *newString;
- (void)metodo2:(NSString *)str{
   newString = str;
}
- (void)metodo3{
   NSLog(newString);
}

L’invocazione del metodo3 causerà un crash della nostra applicazione perchè l’oggetto stringa saluto definita nel metodo1, è di tipo autorelease e quindi verrà rimosso dall’autorelease pool. All’interno del metodo2 l’operazione newString=str non fa altro che assegnare al puntatore newString lo stesso indirizzo di  str che era lo stesso di saluto (ricordiamoci che in fondo parliamo sempre di puntatori C ) quindi quando l’oggetto saluto verrà eliminato i riferimenti all’interno di newString non saranno più validi ed ogni tentativo di accesso produrrà il famoso errore EXC_BAD_ACCESS.

Alla prossima.



Share this story:
  • tweet

Tags: iphonememoriaObjective-coopprogrammazioneteoria

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

  • Programmare a oggetti in TypeScript

    7 Aprile 2017 - 0 Comment
  • Microservizi per logiche applicative di grandi dimensioni

    27 Luglio 2016 - 0 Comment
  • Uno sguardo al runtime Objective-c

    10 Settembre 2013 - 0 Comment

Author Description

12 Responses to “L#008 – Objective-C (Parte III) Gestiamo la memoria”

  1. 3 Dicembre 2009

    Fabrizio

    Ciao, sono un “più che roookie”, non capisco una cosa nell’ultimo esempio: perchè dici che “al puntatore newString lo stesso indirizzo di str che era lo stesso di saluto” ? Dov’è che *saluto si relaziona con str?
    Perdona la domanda banale 🙂 e grazie.

  2. 3 Dicembre 2009

    ignazioc

    Grazie per la segnalazione. Si tratta di un errore nel codice.
    Il primo metodo chiama il secondo con questa riga:
    [classe2 metodo2:saluto]
    non con
    [classe2 metodo2:myString]
    come avevo indicato. (myString non è neanche definita!)
    A questo punto risulta evidente che l’indirizzo di memoria è sempre lo stesso.
    (che vuol dire “più che rookie”??)

  3. 3 Dicembre 2009

    Fabrizio

    Intendevo dire un “pivello” 🙂
    Ok grazie, è chiarissimo ora. Ciao

  4. 3 Dicembre 2009

    Clod75

    Ma siete grandiosi!!!! Questa lezione non solo è utilissima, ma è anche spiegata benissimo!!!! Complimenti!!!

  5. 11 Dicembre 2009

    Antonio

    Salve,

    ho iniziato da poco a programmare su iPhone e nei libri di testo che sto seguendo per una corretta gestione della memoria consigliano di porre a nil gli outlet nel viewDidUnload e rilasciare con release tutti gli attributi di classe in dealloc
    Alcune volte ho visto che in dealloc invece di inviare il messaggio release all’oggetto lo si imposta a nil.

    C’è qualche differenza tra impostare a nil un oggetto o inviare il messaggio di release?

    inoltre in viewDidUnload solo gli outlet devo impostare a nil?

    quando dichiaro nel file header una proprietà con property che differenza c’è tra retain e copy?

    Grazie e complimenti per gli articoli, sono veramente chiari.

  6. 12 Dicembre 2009

    Ignazioc

    tutte domande molto interessanti,
    “C’è qualche differenza tra impostare a nil un oggetto o inviare il messaggio di release?”
    Porre un oggetto uguale a nil in pratica significa prendere un puntatore ad una struttura e farlo puntare a 0, questo significa che la memoria allocata per i membri della struttura non sarà più accessibile, neanche per effettuare il “free”, invece inviare un messaggio di release decrementa il retaincount, quando questi raggiunge il valore 0 viene chiamato (in un momento successivo) in modo automatico il metodo dealloc su questo oggetto dall’autorelease pool.
    È bene specificare che nel caso di classi “personalizzate” è sempre necessario riscrivere il metodo dealloc, per far si che vengano tutte le risorse impegnate all’interno della classe.

    “inoltre in viewDidUnload solo gli outlet devo impostare a nil?”
    la guida dice “viewDidUnload: Called when the controller’s view is released from memory” quindi poi imposture a nil gli outlet come suggerito dalla stessa apple, ma per gli oggetti creati da te devi prevedere metodi specifici.
    ti consiglio di leggere il paragrafo su viewDidUnload sulla UIviewControllerClassReference.

    “quando dichiaro nel file header una proprietà con property che differenza c’è tra retain e copy?”
    retain incrementa il retaincount, mentre copy crea una copia dell’oggetto e si diviene proprietari di questa copia.

  7. 12 Dicembre 2009

    Antonio

    Grazie per la risposta,

    Se ho ben capito la regola che ogni classe è proprietaria e responsabile degli oggetti che crea potrebbe essere descritta dalle seguenti situazioni:

    Assumiamo che stia lavorando con classi e oggetti non personalizzati, ovvero quelli forniti nell’SDK di apple

    impostare a nil quindi vuol dire liberare subito memoria, mentre portare il retainCount a 0 con il messaggio release è come se stessi marcando quell’oggetto come “da eliminare per liberare memoria” e quindi l’autorelease pool in un momento non deterministico richiama il dealloc per quell’oggetto e libera memoria.

    Perciò in un UIViewController porre gli outlet a nil in viewDidUnload, e in dealloc mandare un messaggio di release a tutti gli attributi di classe della view, che questi siano outlet oppure no, è buona norma per una gestione corretta della memoria?

    Se invece lavoro su classi personalizzate, che poi saranno utilizzate nelle view, devo assicurarmi di implementare il metodo dealloc che conterrà il codice per il release di tutti gli oggetti e attributi della classe personalizzata in modo tale da evitare i cosiddetti “memory leaks” che si verrebbero a creare qualora la view che utilizza l’oggetto personalizzato venga deallocata.

  8. 7 Febbraio 2010

    Francesco_96

    Siete fantastici, non ero riuscito a capire bene questa parte dal libro che ho comprato ma con l’aiuto di questo articolo adesso è tutto chiarissimo! Complimenti!!!

  9. 10 Giugno 2010

    Accedere a Flickr dalle nostre applicazioni iPhone (parte 4) | devAPP

    […] leggere questo articolo a tal […]

  10. 14 Luglio 2010

    Gestione della memoria in Objective-C per le nostre applicazioni iPhone, iPad (e Mac) | devAPP

    […] parliamo di gestione della memoria nelle applicazioni iPhone (e iPad). Dopo un primo ottimo articolo sull’argomento, creato dal nostro Ignazio Calò, abbiamo pensato fosse meglio, viste le […]

  11. 23 Luglio 2010

    Mario

    quande che si deve usare il retain invece del assign/copy?

  12. 18 Settembre 2010

    Paolo

    Ciao vorrei chiedervi un aiuto.
    Dichiarando un nsarray nel file vievController.h (header).
    Come faccio ad utilizzarlo correttamente senza errori all’interno del file viewcontroller.m?
    Per esempio all’interno di un evento:
    -(void) dimmi:(id)sender {
    ——–
    }
    Grazie

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