CocoaHeads Toulouse - Patrice Trognon / Delta Studio - ARC
1. Tutorial ARC
Développement de Logiciels & Technologies Avancées
http://www.DLTA-Studio.com
ARC
Tutorial
Patrice Trognon
patrice.trognon@dlta-studio.com
Version 1.1
08/03/2012
Copyright (c) 2012 DLTA Studio
Tous Droits Réservés
Reproduction Interdite
ARC! 1/10
2. Tutorial ARC
Table des Matières
Au début était la gestion tout à la main.! 3
Ensuite nous avons les propriétés! 4
Place à ARC !! 5
Deux nouveaux keywords! 7
ARC et Interface Builder! 8
ARC ne traite que l’objective-C! 9
Désactiver ARC pour la compilation d’un source! 10
ARC! 2/10
3. Tutorial ARC
Au début était la gestion tout à la main.
// Compte.h
@class Client ;
@interface Compte : NSObject {
Client * titulaire ;
}
-(Client*)titulaire ;
-(void)setTitulaire:(Client*)titulaire_;
@end
// Compte.m
@implementation Compte
-(Client*)titulaire {
return titulaire ;
}
-(void)setTitulaire:(Client*)titulaire_ {
[titulaire release];
titulaire = titulaire_;
[titulaire retain];
}
-(void)dealloc {
[titulaire release];
[super dealloc];
}
@end
// test
Client *clientMarcel ; Client *clientPaul; Compte *compte;
clientMarcel = [[Client alloc]init]; // clientMarcel [1], clientPaul [0], compte[0]
clientPaul = [[Client alloc]init]; // clientMarcel [1], clientPaul [1], compte[1]
compte = [[Compte alloc]init]; // clientMarcel [1], clientPaul [1], compte[1]
[compte setTitulaire:clientMarcel]; // clientMarcel [2], clientPaul [1], compte[1]
[compte setTitulaire:clientPaul]; // clientMarcel [1], clientPaul [2], compte[1]
[clientMarcel release]; // clientMarcel [0], clientPaul [2], compte[1]
[clientPaul release]; // clientMarcel [0], clientPaul [1], compte[1]
[compte release]; // clientMarcel [0], clientPaul [0], compte[0]
Toute la gestion de la mémoire s’effectue alors à la main dans la méthode de set et
dans la méthode dealloc, tout est à la charge du développeur, si il oublie une des
instructions il y aura un soucis dans cette gestion (memory-leak).
ARC! 3/10
4. Tutorial ARC
Ensuite nous avons les propriétés
// Compte.h
@class Client ;
@interface Compte : NSObject
@property(nonatomic,retain) Client* titulaire;
@end
// Compte.m
@implementation Compte
@synthesize titulaire;
-(void)dealloc {
[titulaire release];
[super dealloc];
}
@end
// test
Client *clientMarcel ; Client *clientPaul; Compte *compte;
clientMarcel = [[Client alloc]init]; // clientMarcel [1], clientPaul [0], compte[0]
clientPaul = [[Client alloc]init]; // clientMarcel [1], clientPaul [1], compte[1]
compte = [[Compte alloc]init]; // clientMarcel [1], clientPaul [1], compte[1]
[compte setTitulaire:clientMarcel]; // clientMarcel [2], clientPaul [1], compte[1]
[compte setTitulaire:clientPaul]; // clientMarcel [1], clientPaul [2], compte[1]
[clientMarcel release]; // clientMarcel [0], clientPaul [2], compte[1]
[clientPaul release]; // clientMarcel [0], clientPaul [1], compte[1]
[compte release]; // clientMarcel [0], clientPaul [0], compte[0]
La gestion de la mémoire est prise en charge en partie par la propriété, plus besoin
de coder un setter ou un getter ils seront générés par le @synthesize. Attention le
dealloc est toujours présent et à la charge du développeur, si il est oublié il y a
toujours fuite mémoire.
ARC! 4/10
5. Tutorial ARC
Place à ARC !
ARC (Automatic Reference Counting), est la pour aider le développeur afin qu’il n’ait plus
de gestion de mémoire à faire dans son code, du moins afin qu’il en ait beaucoup moins.
Il faut activer ARC à la création du projet.
ARC! 5/10
6. Tutorial ARC
// Compte.h
@class Client ;
@interface Compte : NSObject
@property(nonatomic,strong) Client* titulaire;
@end
// Compte.m
@implementation Compte
@synthesize titulaire;
@end
// test
Client *clientMarcel ; Client *clientPaul; Compte *compte;
clientMarcel = [[Client alloc]init]; // clientMarcel [1], clientPaul [0], compte[0]
clientPaul = [[Client alloc]init]; // clientMarcel [1], clientPaul [1], compte[1]
compte = [[Compte alloc]init]; // clientMarcel [1], clientPaul [1], compte[1]
[compte setTitulaire:clientMarcel]; // clientMarcel [2], clientPaul [1], compte[1]
[compte setTitulaire:clientPaul]; // clientMarcel [1], clientPaul [2], compte[1]
Il n’y a plus une seule ligne de code pour la gestion de la mémoire, ARC va faire le
travail effectué avant par le développeur en générant le code adéquat, le setter et
le getter sont générés par le @synthesize de la propriété comme avant, ARC va
générer le dealloc ainsi que les release quand c’est nécessaire.
Plus de risque de fuite mémoire, gestion plus efficace par ARC car il ne va générer
que ce qu’il faut la ou il faut.
Attention ARC N’EST PAS UN GARBAGE COLLECTOR.
ARC! 6/10
7. Tutorial ARC
Deux nouveaux keywords
// Compte.h
@class Client ;
@interface Compte : NSObject
@property(nonatomic,strong) Client* titulaire;
@end
strong ‘remplace’ retain .
// Case.h
@interface Case : NSObject
@property(nonatomic,weak) Case* suivante;
@end
Dans le cas des références circulaires il faut utiliser weak.
Attention si vous vous passez des propriétés pour utiliser des attributs ils seront
strong par défaut, par exemple ce code va fuir car les références circulaires seront strong :
// Case.h
@interface Case : NSObject {
! Case* suivante;
}
@end
Conclusion
1/ Il faut utiliser les propriétés
2/ utiliser weak pour les références circulaires
ARC! 7/10
8. Tutorial ARC
ARC et Interface Builder
Il faut distinguer ici deux cas, les projets qui ciblent uniquement iOS 5, et ceux qui ciblent
iOS 5 et iOS 4.
Dans le cas d’iOS 5 les IBOutlet(s) doivent être déclarées en weak.
@property(nonatomic,weak) IBOutlet UILabel * label ;
Dans le cas d’iOS 4 weak n’étant pas supporté il faut les déclarer strong.
@property(nonatomic,strong) IBOutlet UILabel * label ;
ARC! 8/10
9. Tutorial ARC
ARC ne traite que l’objective-C
Attention lors de l’utilisation d’API C, typiquement toutes les API Core, il faut utiliser les
opérateur de cast spécifiques à ARC.
Par exemple sans arc on avait ce code :
+ (NSString*) createUUID {
NSString *udid;
CFUUIDRef uuidRef = CFUUIDCreate(0);
CFStringRef uuidStringRef = CFUUIDCreateString(NULL,uuidRef);
udid = [NSString stringWithString:(NSString*)uuidStringRef];
CFRelease(uuidStringRef);
CFRelease(uuidRef);
return udid ;
}
Avec ARC il devient :
+ (NSString*) createUUID {
NSString *udid;
CFUUIDRef uuidRef = CFUUIDCreate(0);
CFStringRef uuidStringRef = CFUUIDCreateString(NULL,uuidRef);
udid = [NSString stringWithString:(__bridge NSString*)uuidStringRef];
CFRelease(uuidStringRef);
CFRelease(uuidRef);
return udid ;
}
Ici ARC ne devient pas responsable de uuidStringRef, qui est libéré par le CFRelease,
donc tout va bien.
ou :
+ (NSString*) createUUID {
NSString *udid;
CFUUIDRef uuidRef = CFUUIDCreate(0);
CFStringRef uuidStringRef = CFUUIDCreateString(NULL,uuidRef);
udid = [NSString stringWithString:(__bridge_transfer NSString*)uuidStringRef];
CFRelease(uuidStringRef);
CFRelease(uuidRef);
return udid ;
}
Dans ce second cas ARC devient responsable de uuidStringRef.
Attention, il est donc libéré deux fois puisque le CFRelease le fait aussi, donc crash !
On remarque que ARC ne s’occupant pas de toute ce qui est alloué par
Core on a toujours les CFRelease !
La bonne pratique consiste a laisser ARC traiter tout le code Objective-
C, et traiter son code C comme d’habitude, c’est à dire avec les
CFRelease nécessaires, chacun son boulot !
La première approche qui consiste à utiliser un cast (__bridge
NSString*) est donc préférable, en oubliant pas le CFRelease après bien
sur !
ARC! 9/10
10. Tutorial ARC
Désactiver ARC pour la compilation d’un source
Il est possible de désactiver ARC, cela va se faire dans les préférences du projet, sur la
Target dans l’onglet Build Phases, pour le source sur lequel on ne souhaite pas utiliser
ARC il suffit d’ajouter le flag de compilation -fno-objc-arc
Patrice Trognon <patrice.trognon@dlta-studio.com>
ARC! 10/10