• 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

Introduzione ad ARC (Automatic Reference Counting)

By Valerio Dutto | on 20 Ottobre 2011 | 13 Comments
Senza categoria

guide-objective-c Il nuovo compilatore Apple LLVM 3.0, introdotto con Xcode 4.2, introduce una funzionalità chiamata Automatic Reference Counting (ARC) estremamente interessante per noi sviluppatori. Essa ci aiuta a gestire in modo corretto la memoria di un’applicazione, permettendoci di creare applicazioni migliori e più robuste.

Prima di ARC, dovevamo chiamare manualmente e nei posti giusti i metodi retain/release/autorelease per assicurarci che gli oggetti rimanessero “in vita” esattamente quanto ci serviva. Purtroppo, dimenticare o inserire nel posto errato anche solo una chiamata a questi metodi dava origine a sprechi di memoria (che tipicamente crescono man mano che si usa l’applicazione) o addirittura a crash, cosa assolutamente intollerabile per un’applicazione professionale.

Grazie ad ARC, gran parte di questo lavoro viene ora fatto in automatico e in modo migliore di quanto potremmo fare noi stessi. Vediamo come e perché.

Introduzione

Innanzitutto occorre fare una precisazione: ARC non è un garbage collector. Il garbage collector funziona a run-time, cioè durante l’esecuzione del programma. ARC, al contrario, lavora a compile-time, cioè durante la compilazione. La differenza è fondamentale: vediamo perché.

Il garbage collector è a tutti gli effetti un “processo” che analizza continuamente la memoria alla ricerca di zone che non vengono più utilizzate e che dunque possono essere deallocate. Questo ha le seguenti implicazioni:

  1. Il garbage collector consuma cicli di CPU.
  2. L’istante in cui un oggetto verrà rilasciato non è predicibile.

ARC, invece, lavora a compile-time. In pratica, va lui stesso ad aggiungere al posto nostro le istruzioni necessarie per la gestione della memoria. D’altronde: chi meglio del compilatore sa dove devono essere inserite?

ARC, dunque, per certi versi è meglio di un garbage collector, anche se vedremo che ha alcuni limiti (per i più impazienti: non gestisce i retain cycle).

ARC è disponile solo a partire da Xcode 4.2 e solo quando si usa il compilatore Apple LLVM 3.0. Inoltre, il deployment target deve essere impostato almeno a iOS 4 o a OS X 10.6, sebbene alcune funzionalità (le weak reference) siano supportate solo a partire da iOS 5 e OS X 10.7. ARC, inoltre, può gestire solo oggetti e blocchi Objective-C, dunque se per qualche motivo ci trovassimo ad utilizzare le vecchie malloc dovremo continuare a liberarne la memoria manualmente.

In dettaglio

Il compilatore, quando ARC è abilitato, sintetizza in automatico le chiamate ai metodi retain/release/autorelease. Ad esempio questo codice:

Foo *foo = [[Foo alloc] init];
// do something with foo
return;

viene automaticamente trasformato in:

Foo *foo = [[Foo alloc] init];
// do something with foo
objc_release(foo); // funzionalmente equivalente alla vecchia [foo release];
return;

In pratica non dobbiamo più ricordarci di mettere la release, perché ci pensa il compilatore. Non solo: abbiamo la garanzia che lui la metta nel punto giusto, ovvero quando la variabile non viene più utilizzata, non un attimo prima e non un attimo dopo.

Un bel vantaggio: ARC riduce la possibilità che si verifichino quei nostri errori che si manifestavano in memory leak (zone di memoria occupate e mai più rilasciate), dangling pointer (puntatori che si riferiscono ad aree di memoria non più valide), double free (zone di memoria rilasciate più volte) e addirittura crash dell’applicazione.

Una curiosità. Perché il compilatore ha inserito una chiamata a funzione, objc_release(), al posto di una chiamata al metodo release? Semplicemente perché, pur essendo funzionalmente equivalente, una chiamata a funzione viene eseguita più rapidamente rispetto all’invio di un messaggio. Non solo: il compilatore la può individuare con maggiore semplicità e fare delle ulteriori ottimizzazioni. In questo articolo non ci interessano i dettagli, però è interessante osservare che il codice scritto con ARC tipicamente è addirittura più efficiente di quello che potremmo scrivere a mano.

Ma torniamo a noi. ARC permette di rimuovere molto codice che prima eravamo obbligati a scrivere. Ad esempio, nella maggior parte dei casi potremo evitare di scrivere i vecchi metodi dealloc, perché il compilatore è in grado di sintetizzarli per noi. Addio dunque a queste vecchie istruzioni:

- (void)dealloc {
    [foo release];
    [bar release];
    [baz release];
    [qux release];

    [super dealloc];
}

Non si tratta di un vantaggio da poco. Quante volte, ad esempio, ci è capitato di dimenticare di rilasciare una variabile di istanza o di chiamare il metodo dealloc su super?

In sintesi, usando ARC abbiamo questi vantaggi:

  1. Codice più compatto e meno istruzioni da scrivere.
  2. Codice più veloce.
  3. Codice con meno errori e dunque meno memory leak.

Prima di concludere il paragrafo, vogliamo evidenziare che con ARC non solo non serve più chiamare i vecchi metodi retain, release, autorelease e retainCount, ma addirittura diventa vietato. Infatti, se provassimo ad inserirli ci compariranno degli errori di compilazione piuttosto espliciti, come ad esempio “ARC forbids explicit message send of ‘retain'”.

Introduzione ai nuovi tipi di ownership

Prima di procedere dobbiamo fare una parentesi più tecnica, fondamentale per capire il seguito dell’articolo e per padroneggiare davvero ARC.

ARC introduce questi nuovi “lifetime qualifier” per gli oggetti:

  • strong
  • weak
  • unsafe unretained
  • autoreleasing

Essi si usano nelle dichiarazioni in questo modo:

Foo __strong *obj1 = ...;
Foo __weak *obj2 = ...;
Foo __autoreleasing *obj3 = ...;
Foo __unsafe_unretained *obj4 = ...;

In estrema sintesi:

  • strong è il qualificatore di default.
  • weak specifica una weak reference che viene automaticamente posta a nil quando l’oggetto a cui punta è stato deallocato.
  • unsafe unretained è simile a weak, ma non viene automaticamente posto a nil.
  • autoreleasing denota gli argomenti che sono passati per riferimento e che sono rilasciati automaticamente dopo il return.

Spiegazione un po’ fumosa? Facciamo un esempio pratico. Consideriamo questo codice:

Foo *obj1 = [[Foo alloc] init];
Foo *obj2 = obj1;
Foo __unsafe_unretained *obj3 = obj1;
// do something with obj1, obj2 and obj3
return;

Come viene “tradotto” da ARC? Indicativamente così:

Foo __strong *obj1 = [[Foo alloc] init];
Foo __strong *obj2 = objc_retain(obj1);
Foo __unsafe_unretained *obj3 = obj1;
// do something with obj1, obj2 and obj3
objc_release(obj1);
objc_release(obj2);
return;

Innanzitutto osserviamo che obj1 e obj2 vengono entrambi marcati con il qualificatore strong. Perché? Perché non avendone specificati altri viene usato questo.

Essendo obj2 di tipo strong, ARC vi aggiunge una retain esplicita sull’oggetto puntato. In altre parole, sia obj1 sia obj2 diventano “proprietari” dell’oggetto da essi puntato. Naturalmente, dal momento che entrambi obj1 e obj2 sono diventati proprietari dell’oggetto, dovranno poi rilasciarlo quando non gli servirà più. Ma per fortuna non dobbiamo occuparcene noi: è proprio il compito di ARC, che inserisce le chiamate objc_release() appena prima di uscire dallo scope, ovvero appena prima della return. Ecco quindi svelato il significato del qualificatore strong: una variabile è marcata in questo modo quando è ARC a gestirne la memoria.

obj3 invece, essendo unsafe unretained, si comporta in modo diverso. Come possiamo osservare, obj3 non diventa “proprietario” dell’oggetto puntato, perché non fa su di esso una retain esplicita. In pratica, quando dichiariamo una variabile unsafe unretained stiamo dicendo ad ARC che non deve occuparsi lui della gestione della memoria. Attenzione però: quando entrambi obj1 e obj2 saranno stati rilasciati, obj3 continuerà a puntare ad un’area di memoria non valida. Poco grave in questo esempio specifico, ma è una cosa a cui conviene prestare molta attenzione. Per fortuna ci viene in aiuto il qualificatore weak. Usandolo al posto di unsafe unretained, ARC farebbe la “traduzione” in questo modo:

Foo __strong *obj1 = [[Foo alloc] init];
Foo __strong *obj2 = objc_retain(obj1);
Foo __weak *obj3 = obj1;
// do something with obj1, obj2 and obj3
objc_release(obj1);
objc_release(obj2);
obj3 = nil; // <---------
return;

Come possiamo vedere, dopo che entrambi obj1 e obj2 sono stati rilasciati, obj3 viene automaticamente posto a nil. Un bel vantaggio, anche se per poterne usufruire dobbiamo avere come target almeno iOS 5 o OS X Lion.

Rissumendo, quindi, se vogliamo che ARC si occupi lui della gestione della memoria dobbiamo dichiarare l'oggetto come strong (oppure non dire niente, dal momento che questo è il comportamento di default). Se invece vogliamo occuparcene noi, dobbiamo usare il qualificatore weak oppure, sui vecchi iOS 4 e Snow Leopard, unsafe unretained.

A questo punto ci è sicuramente sorto un dubbo: ma perché dovremmo usare weak o unsafe unretained? Perché non possiamo semplicemente demandare ad ARC il problema della gestione della memoria di tutte le variabili? Per via dei cosiddetti "retain cycle".

Retain cycle

Un "retain cycle" si presenta quando due oggetti si fanno (direttamente o indirettamente) una retain a vicenda. Questo comporta un memory leak, perché entrambi gli oggetti continuerebbero a "vivere" per tutta la durata dell'applicazione anche se non venissero più utilizzati da nessun'altro.

Il più semplice retain cycle che si può verificare è questo:


Introduzione-ad-ARC-Automatic-Reference-Counting-01

I nomi scelti per l'esempio non sono casuali. Un classico caso di retain cycle sono proprio i delegati. In tal caso, un oggetto (Foo nel nostro caso) mantiene un riferimento al suo delegato (FooDelegate). Se anche il delegato mantiene un riferimento di tipo strong a Foo (che è appunto il comportamento di default), nessuno dei due verrebbe mai rilasciato:


Introduzione-ad-ARC-Automatic-Reference-Counting-02

Come possiamo risolvere il problema? Usando le "weak reference":


Introduzione-ad-ARC-Automatic-Reference-Counting-03

Guardando il problema da un punto di vista più generale, le linee guida Apple consigliano che nel caso in cui due oggetti abbiano una relazione padre-figlio, il genitore debba mantenere un riferimento di tipo strong verso il figlio. Se il figlio necessita di un riferimento al padre questo deve invece essere di tipo weak. In tal modo scampiamo alla trappola del retain cycle.

Alcune volte i retain cycle sono più subdoli. È raro (ma non impossibile) avere catene di tre, quattro, cinque o più oggetti che puntano l'un l'altro in un circolo vizioso. Meglio starci attenti, quindi.

Come cambiano le proprietà

Quanto visto vale per gli oggetti e le variabili, ma anche per le proprietà esiste un approccio del tutto analogo.

Il vecchio codice:

@property (retain) FooDelegate *obj;

adesso dovrà diventare:

@property (strong) FooDelegate *obj;

Questo invece:

@property (assign) Foo *parentObj;

diventa:

@property (weak) Foo *parentObj;

Le regole più importanti

Alla luce di quanto detto, ecco le regole da seguire per poter compilare con ARC abilitato:

  1. alloc/init degli oggetti: la creazione degli oggetti risulta invariata ma non si possono più fare chiamate (dirette o indirette usando @selector) ai metodi retain/release/autorelease/retainCount.
  2. Metodi di deallocazione: vengono di norma creati automaticamente e, come sempre, non va mai chiamata direttamente la dealloc. Se fosse necessario rilasciare altre risorse (allocate ad esempio con le vecchie malloc), si può creare un metodo dealloc senza chiamare la [super dealloc], in quanto viene automaticamente aggiunta da ARC.
  3. Dichiarazione delle proprietà: utilizzare le parole chiave strong/weak in sostituzione di retain/copy/assign. strong lo usiamo quando vogliamo demandare ad ARC il problema della gestione della memoria. weak (o unsafe unretained sui vecchi iOS 4 e Snow Leopard) quando dobbiamo evitare dei retain cycle.

Migrazione

Per rendere un vecchio codice compatibile con ARC, Xcode 4.2 ci fornisce un ottimo strumento semi-automatizzato. Lo troviamo a partire dal menù Edit > Refactor > Convert to Objective-C ARC:


Introduzione-ad-ARC-Automatic-Reference-Counting-04

Il convertitore dapprima imposterà il compilatore Apple LLVM 3.0, che come dicevamo nell'introduzione è l'unico che attualmente supporta ARC. Questo passaggio tipicamente comporta il sorgere di errori o warning che dovremo correggere.

Successivamente il codice verrà reso compatibile con ARC, rimuovendo le chiamate ai metodi retain, release e autorelease e inserendo gli opportuni lifetime qualifier nella dichiarazione delle property. Inoltre, ogni NSAutoreleasePool verrà sostituito con il nuovo statement @autoreleasepool.

Coesistenza di codice ARC con codice non-ARC

Prima di concludere osserviamo che è possibile far coesistere codice ARC con codice non-ARC (interi framework, ma anche singoli file). Non entreremo nei dettagli in questo articolo, ma si tratta di una cosa molto utile per poter continuare ad utilizzare vecchie librerie stabili e consolidate negli anni senza doverle necessariamente migrare ad ARC.

Riferimenti

In rete purtroppo non vi sono ancora molte informazioni su ARC. I pochi articoli presenti sono estremamente tecnici. Ad ogni modo, a chi volesse approfondire l'argomento consigliamo questi:

  • Automatic Reference Counting
  • Transitioning to ARC Release Notes

Autori

Valerio Dutto e Marco Rocca sono i fondatori di Delite Studio S.r.l., società specializzata nella creazione di applicazioni native di alta qualità per OS X e iOS. Il loro sito Web è http://www.delitestudio.com.

Share this story:
  • tweet

Tags: ARCAutomatic Reference Countingdangling pointerdouble freegarbage collectorGuide varieMemory Leakretain cycleValerio Dutto

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

  • Xcode 4: scopriamo come possiamo usare l’analizzatore statico per trovare alcuni bug nel nostro codice

    27 Maggio 2011 - 6 Comments
  • Xcode 4: scopriamo come pubblicare un’applicazione su App Store e Mac App Store

    9 Maggio 2011 - 29 Comments
  • Xcode 4: scopriamo che cosa sono gli snippet e come possiamo usarli in modo efficace

    28 Aprile 2011 - 6 Comments

Author Description

Valerio Dutto. Ingegnere informatico con oltre 5 anni di esperienza nella realizzazione di software professionali. Ha fondato, insieme a Marco Rocca, Delite Studio S.r.l., società specializzata nella creazione di applicazioni native di alta qualità per OS X e iOS. Il loro sito Web è http://www.delitestudio.com.

13 Responses to “Introduzione ad ARC (Automatic Reference Counting)”

  1. 20 Ottobre 2011

    Introduction to Objective-C Automatic Reference Counting (ARC) | Delite Studio

    […] Our article on the Objective-C Automatic Reference Counting (in italian) is now live on devApp.it website. […]

  2. 20 Ottobre 2011

    simone

    Wow, grazie mille della spiegazione molto chiara. Ero proprio curioso di capire come funziona questa nuova funzionalità e in rete non c’è in effetti ancora molto e certamente non così chiaro e in italiano!

  3. 20 Ottobre 2011

    milonet

    molto bello l’articolo e molto utile.. complimenti agli autori! ammetto che alcuni argomenti sono avanzati per un neofita come me ma devo dire che è cmq chiaro!

  4. 20 Ottobre 2011

    Andrea

    Complimenti ottimo articolo….

    Ne aproffito per chiedere una cosa è possibile vedere il codice che genera quando si ulitizza arc?

  5. 21 Ottobre 2011

    Valerio Dutto

    Grazie a tutti per i complimenti!

    Andrea, purtroppo ad oggi non mi risulta che vi sia alcuna opzione da passare al compilatore per vedere l’output di ARC. O meglio: nessuna opzione documentata.

    A presto,

    Valerio.

  6. 21 Ottobre 2011

    Ant

    Scusate mi se scrivo qui e no nel forum sono con l’iPhone

  7. 21 Ottobre 2011

    Giuseppe Frattura

    Ciao ho letto l’articolo e lo trovo davvero interessante, con questa feature si porta il linguaggio ad essere facile come il java ma al tempo stesso potente come il c++.

  8. 21 Ottobre 2011

    Francesco

    Complimenti, articolo veramente completo.

  9. 23 Ottobre 2011

    Danilo

    Finalmente!! Chi programma in tanti linguaggi come me finalmente può dire che qualcosa di troppo da ricordare ora è solo un “lontano ricordo” 😉

  10. 24 Ottobre 2011

    Nicholas

    Ottimo articolo, complimenti è stato molto utile!

  11. 24 Ottobre 2011

    Luca

    Ero solito seguire questo tutorial per fare recensire la mia app utilizzando “Appirater” http://www.devapp.it/wordpress/t096-facciamoci-lasciare-una-recensione-in-app-store-dalla-nostra-applicazione-con-appirater.html
    molto facile e comodo.. adesso ho provato a rimetterlo in una nuova app e facendo il refactor ottengo 1 errore, ripetuto 2 volte: Semantic Issue: ‘NSAutoreleasePool’ is unavailable: not available in automatic reference counting mode …. nel codice originale ho NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; e il refactor lo corregge in @autoreleasepool e da’ comunque errore… e poi stranamente compila.. ma non ho Appirater che funziona…..

  12. 4 Novembre 2011

    Andrea Cappellotto

    il refactor non sistema tutte le cose, anzi la maggior parte vanno sistemate a mano. Buona lezione, ma è solo uno spunto da approfondire, mancano i riferimenti a __briged. se volete un consiglio leggete questo http://www.raywenderlich.com/store/ios-5-by-tutorials
    (penso k l’autore abbia preso spunto da qui)


    Luca:

    Ero solito seguire questo tutorial per fare recensire la mia app utilizzando “Appirater” http://www.devapp.it/wordpress/t096-facciamoci-lasciare-una-recensione-in-app-store-dalla-nostra-applicazione-con-appirater.html
    molto facile e comodo.. adesso ho provato a rimetterlo in una nuova app e facendo il refactor ottengo 1 errore, ripetuto 2 volte: Semantic Issue: ‘NSAutoreleasePool’ is unavailable: not available in automatic reference counting mode …. nel codice originale ho NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; e il refactor lo corregge in @autoreleasepool e da’ comunque errore… e poi stranamente compila.. ma non ho Appirater che funziona…..

  13. 9 Novembre 2011

    Mattia Campolese

    Ottimo articolo. Una domanda, come comportarsi con il vecchio “copy” utilizzato nelle stringhe? (parlo di @property (nonatomic, copy) NSString *testM)

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