Rispetto a quanto detto finora, ARC ha uno svantaggio rispetto al Garbage Collector di Java.
Infatti, se definiamo 2 oggetti A e B tali che A referenzia B e B referenzia A, abbiamo creato un ciclo di referenza. Questo significa che entrambi gli oggetti risulteranno referenziati e quindi non verranno mai rilasciati da ARC, anche se dalla nostra applicazione abbiamo perso referenze ad essi.
Vediamo un esempio pratico.
Aggiorniamo la classe Author come segue:
File Author.h
#import <Foundation/Foundation.h>
@class Book;
@interface Author : NSObject
- (instancetype)initWithName:(NSString*)name andBook:(Book *)book;
@property (retain, readonly) NSString * name;
@property (retain, readwrite) Book * book;
@end
File Author.m
#import "Author.h"
#import "Book.h"
@implementation Author
- (instancetype)initWithName:(NSString*)name andBook:(Book *)book {
if (self = [super init]) {
_name = name;
_book = book;
}
return self;
}
-(void)dealloc {
NSLog(@"did dealloc author");
}
@end
Infine, aggiorniamo main.m come segue:
@import Foundation;
#import "Author.h"
#import "Book.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
for (int i = 0; i < 3; i++) {
NSLog(@"Start loop");
Book * book = [[Book alloc] initWithTitle:@"The Naked Sun"];
book.author = [[Author alloc] initWithName:@"Isaac Asimov"
andBook:book];
NSLog(@"End loop");
}
}
return 0;
}
Come si può notare, adesso abbiamo creato un ciclo di referenze. Infatti, l’oggetto book referenzia l’oggetto di tipo Author, che a sua volta referenzia book.
Questo significa che i 2 oggetti non verranno più rilasciati dalla memoria, anche quando avremo perso le referenze per raggiungerli. Se eseguiamo il codice e osserviamo il log, otteniamo quanto segue:
Start loop
End loop
Start loop
End loop
Start loop
End loop
Come si vede, i metodi dealloc
di Author e Book non sono stati invocati, e quindi gli oggetti sono rimasti in memoria durante tutta l’esecuzione dell’applicazione.
Questo scenario è molto pericoloso perché può portare l’applicazione a richiedere una quantità eccessiva di memoria finché il sistema operativo deciderà di terminarla. Per risolvere questo problema dobbiamo creare delle referenze di tipo weak
, ovvero che non vengano conteggiate da ARC.
Vediamo come modificare il file Author.h:
#import <Foundation/Foundation.h>
@class Book;
@interface Author : NSObject
- (instancetype)initWithName:(NSString*)name andBook:(Book *)book;
@property (retain, readonly) NSString * name;
@property (weak, readwrite) Book * book;
@end
In questo caso, abbiamo dichiarato la property book come weak
. Da un punto di vista concettuale abbiamo adesso una situazione di questo tipo.
Questo ci permette di evitare uno strong reference cycle, ovvero un ciclo di referenze forti. Se eseguiamo nuovamente il codice, vedremo che gli oggetti vengono nuovamente deallocati correttamente:
Start loop
End loop
did dealloc book
did dealloc author
Start loop
End loop
did dealloc book
did dealloc author
Start loop
End loop
did dealloc book
did dealloc author