Post on 25-May-2015
description
Bien coderSur iOS
Julien QUEREjulien@cocoaheads.fr
CocoaHeads Rennes8 septembre 2011
Pour qui ? Pourquoi ?Pour l’utilisateur
✓Compréhensibilité
✓Flexibilité
✓Réutilisabilité
‣ Maintenance et évolutions plus aisés
‣ Projets plus rapides
➡Gain de temps
➡Gain en réactivité
Pour le futur✓ Performance
✓ Stabilité
‣ Moins de bugs
‣ Moins de retours
‣ Une meilleure expérience utilisateur/client
➡ Gain de temps
➡ Gain en crédibilité
J.
« L’important, ce n’est pas les règles, c’est le fait d’en avoir »
Coding guidelines
Formatage- (NSURL*) url {! NSString * url = [NSString stringWithString:kWDGdirectionsBaseURL];! NSDictionary * parameters = [self parameters];!! if(parameters)! {! BOOL isFirstKey = YES;! ! ! ! NSString * separator = @"?";! !! ! ! for (NSString * key in [parameters allKeys]) {! ! ! url = [url stringByAppendingFormat:@"%@%@=%@", separator, key, [parameters valueForKey:key]];! ! !! ! ! if(isFirstKey)! ! {! ! ! isFirstKey = NO;! ! ! ! separator = @"&";! ! ! }! ! }! ! }! ! ! ! ! url = [url stringByAddingPercentEscapesUsingEncoding:! ! NSUTF8StringEncoding];! return [NSURL URLWithString:url];}
Formatage- (NSURL*) url {! NSString * url = [NSString stringWithString:kWDGdirectionsBaseURL];! NSDictionary * parameters = [self parameters];!! if(parameters) {! ! BOOL isFirstKey = YES;! ! NSString * separator = @"?";! !! ! for (NSString * key in [parameters allKeys]) {! ! ! url = [url stringByAppendingFormat:@"%@%@=%@", separator, key, [parameters valueForKey:key]];! ! !! ! ! if(isFirstKey) {! ! ! ! isFirstKey = NO;! ! ! ! separator = @"&";! ! ! }! ! }! }! url = [url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];! return [NSURL URLWithString:url];}
Formatage
➡ Un code propre = code compréhensible
➡ Surveiller: l’indentation, le placement des délimiteurs, ...
NommageIl faut pouvoir facilement différencier:
➡ Une variable statique
➡ Une variable d’instance
➡ Un attribut
➡ Une variable de classe
➡ Une variable locale
La règle de 3
➡ Je développe une routine une fois
➡ Seconde fois: je me pose des questions
➡ Troisième fois: je factorise
- (void) displayTopTweets:(NSArray*)tweets {
!
!
! !
! !}
! if([tweets count] == 4) {! ! [tweetButton1_.titleLabel setNumberOfLines:2];! ! [tweetButton1_ setTitle:[tweets objectAtIndex:0]! ! ! ! ! forState:UIControlStateNormal];! ! [tweetButton1_ setTitle:[[tweets objectAtIndex:0] uppercaseString]! ! ! ! ! forState:UIControlStateHighlighted];! ! [tweetButton1_ setHidden:NO];!! ! [tweetButton2_.titleLabel setNumberOfLines:2];! ! [tweetButton2_ setTitle:[tweets objectAtIndex:1]! ! ! ! ! forState:UIControlStateNormal];! ! [tweetButton2_ setTitle:[[tweets objectAtIndex:1] uppercaseString]! ! ! ! ! forState:UIControlStateHighlighted];! ! [tweetButton2_ setHidden:NO];! !! ! [tweetButton3_.titleLabel setNumberOfLines:2];! ! [tweetButton3_ setTitle:[tweets objectAtIndex:2]! ! ! ! ! forState:UIControlStateNormal];! ! [tweetButton3_ setTitle:[[tweets objectAtIndex:2] uppercaseString]! ! ! ! ! forState:UIControlStateHighlighted];! ! [tweetButton3_ setHidden:NO];! !! ! [tweetButton4_.titleLabel setNumberOfLines:2];! ! [tweetButton4_ setTitle:[tweets objectAtIndex:3]! ! ! ! ! forState:UIControlStateNormal];! ! [tweetButton4_ setTitle:[[tweets objectAtIndex:3] uppercaseString]! ! ! ! ! forState:UIControlStateHighlighted];! ! [tweetButton4_ setHidden:NO];!!! }
- (void) displayTopTweets:(NSArray*)tweets {
!
!
! !
! !
! ! ! [button.titleLabel setNumberOfLines:2];! ! ! [button setTitle:tweet! ! ! ! ! forState:UIControlStateNormal];! ! ! [button setTitle:[tweet uppercaseString]! ! ! ! ! forState:UIControlStateHighlighted];! ! ! [button setHidden:NO];
! NSArray * buttons = [NSArray arrayWithObjects:tweetButton1_, tweetButton2_, ! ! ! ! ! ! tweetButton3_, tweetButton4_, nil];
! if([buttons count] == [tweets count]) {! ! for (NSUInteger i = 0; i < [buttons count]; i++) {
! ! ! ! ! }! }
! ! ! UIButton * button = [buttons objectAtIndex:i];! ! ! NSString * tweet = [tweets objectAtIndex:i];
}
Utiliser des constantes
➡ Pas de valeur numérique / textuelle «en dur»
➡ Utiliser des constantes
➡ Utiliser un fichier de locales
Définir ses propres guidelines
➡ Prendre le temps et du recul
➡ Intégrer toute l’équipe dans le processus
➡ Ne pas hésiter à se baser sur des guidelines existantes
http://google-styleguide.googlecode.com/svn/trunk/objcguide.xml
Google Objective-C Style Guide
Jean-Pierre Florian
« Quand on est pauvre, on n'a que la ressource d'être sage. »
Gestion intelligente des ressources
Quelles ressources ?
Batterie Echanges réseau
Mémoire
Batterie
➡ CPU
➡ Radio (Wi-Fi, Bluetooth, GPS, EDGE/3G)
➡ Accéléromètre / gyroscope
➡ Boussole
➡ Disque (stockage flash)
➡ Ecran
Pour gagner en autonomie, iOS désactive le materiel dés que possible:
C’est en réduisant l’usage de ces matériels que l’on
augmente l’autonomie
Batterie: L’exemple Core Location
➡ Solution simple: - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ! manager_ = [[CLLocationManager alloc] init];! manager_.desiredAccuracy = kCLLocationAccuracyBestForNavigation;! manager_.delegate = self;! [manager_ startUpdatingLocation];
➡Core Location va mettre à jour la position en continu ...
➡... jusqu'à ce que la batterie soit vide
➡ Core Location va mettre à jour la position en continu ...
➡ ... jusqu'à ce que la batterie soit vide
Batterie: L’exemple Core Location
➡ Solution économique:
➡ Ne lancer Core Location que quand c’est nécessaire
➡ Régler la précision avec intelligence
➡ Ne pas laisser tourner Core Location inutilement
Batterie - Mais aussi➡ Eviter tout calcul inutile
➡ Ne jamais utiliser [application setIdleTimerDisabled:YES] sans raison
➡ Eviter les accès trop fréquents au disque
➡ Ne dessiner que ce qui est nécessaire
➡ Utiliser l'accéléromètre / gyroscope avec autant de parcimonie que Core Location
➡ Limiter les accès réseau
Le JSON est 52% moins lourd que le XML
Réseau
➡ Choisissez bien votre format !
➡ Exemple avec recherche twitter
0 Ko
5 Ko
10 Ko
14 Ko
19 Ko
Taille de la réponse
9,47 Ko
18,05 Ko
http://search.twitter.com/search.json?q=cocoaheads&result_type=mixed&count=100
Données calculées sur la moyenne de 100 appels à l’API Twitter en JSON vs XML
XML JSON
Réseau
➡ Ne prendre ce qui est nécessaire
➡ Pourquoi télécharger 100 tweets sur les CocoaHeads alors qu’on en affichera que 15 ?
➡ Astuce: utiliser la pagination
➡ Mais ne pas en abuser: éviter de trop nombreuses petites requêtes
Réseau
➡ Malheureusement, on n’a pas toujours la main sur la plateforme Web
➡ Il faut s’en accommoder
➡ Commencer la réflexion en partant du mobile dès que possible
Walter Prévost
« La mémoire, c'est comme une valise. On met toujours dedans des choses qui ne
servent à rien.»
La mémoire
La mémoire
➡ Objectif: réduire son emprunte mémoire:
➡ Consommer le moins de mémoire, le moins longtemps possible.
La mémoire
0 Mo
32 Mo
64 Mo
96 Mo
128 Mo
58 Mo
4 Mo10 Mo
12 Mo
32 Mo
12 Mo
Source: Apple TechTalks Paris 2009 Valeurs pour un iPhone 3G
Plus les appareils montent en gamme plus il y a de mémoire.
Cependant, iOS est de plus en plus gourmand avec le temps
Couche graphiqueKernelDémonsSpringboardTéléphoneDisponible
La mémoire
➡ Principales sources de « gaspillage »:
➡ Les fuites
➡ La mémoire allouée inutilement
➡ La mémoire libérée trop tard
La mémoire
Rappel: 1 retain / alloc / copy = 1 release
Gare à l’autoRelease !
➡ Tout objet en autorelease est placé dans le bassin le plus «proche»
➡ Quand le bassin est vidé, il envoie un Release à chacun des objets qu’il gère
L’autorelease très pratique, mais il faut redoubler de vigilance
Gare à l’autoRelease ! NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; TBXML * tbxml = [[TBXML alloc] initWithXMLData:xmlData]; TBXMLElement *root = tbxml.rootXMLElement; if (root) { TBXMLElement *item = [TBXML childElementNamed:@"item" parentElement:root]; while (item != nil) { // Parsing code item = [TBXML nextSiblingNamed:@"item" searchFromElement:item]; } } [tbxml release]; [pool release];
0MB
13MB
25MB
38MB
50MB
5MB
20MB
35MB
49MB
5MB 5MB 5MB
Parsing d’un fichier XML basé sur le Top 300 iTunes avec TBXML 1.4.Mesures effectuées avec instruments.
TBXML * tbxml = [[TBXML alloc] initWithXMLData:xmlData]; TBXMLElement *root = tbxml.rootXMLElement; if (root) { TBXMLElement *item = [TBXML childElementNamed:@"item" parentElement:root]; while (item != nil) {
Gare à l’autoRelease !
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; // Parsing code item = [TBXML nextSiblingNamed:@"item" searchFromElement:item]; } } [tbxml release];
[pool release];
0MB
13MB
25MB
38MB
50MB
5MB 7MB12MB 12MB
5MB 5MB 5MB5MB
20MB
35MB
49MB
5MB 5MB 5MB
Une gestion fine de l’autorelease, c’est une gestion fine de la mémoire
Parsing d’un fichier XML basé sur le Top 300 iTunes avec TBXML 1.4.Mesures effectuées avec instruments.
Le Dealloc
➡ Un dealloc vide ou incomplet: c’est la garantie de fuites mémoire.
➡ Astuce 1: A la création d’une variable d’instance, on la met de suite dans le dealloc (si il y a lieu).
➡ Astuce 2: Mettre les variable dans le même ordre dans l’interface que dans le dealloc.
➡ Ne jamais oublier le [super dealloc];
Be lazy
- (IBAction) showCredits:(UIButton*) sender {! [self displayTextPopup:credits_];}- (void) dealloc {! [credits_ release];! [super dealloc];}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self != nil) {
} return self;}
credits_ = [[NSString stringWithContentsOfFile:filePath] retain];
! ];! [self credits]
Be lazy
- (IBAction) showCredits:(UIButton*) sender {! [self displayTextPopup:}- (void) dealloc {! [credits_ release];! [super dealloc];}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self != nil) {
} return self;}- (NSString*)credits {! if(!credits_) {
! }!! return credits_;}
credits_ = [[NSString stringWithContentsOfFile:filePath] retain];
Memory warning
➡ Le memory warning signifie que le système va être à court de mémoire.
➡ Soyez courtois, et libérez tout ce que vous pouvez.
➡ Rappelez vous, le système a la License To Kill.
Memory warning
Trois options pour être tenu au courant:
➡ Dans un ViewController: - (void) didReceiveMemoryWarning;
➡ Dans l’UIApplicationDelegte:
- (void) applicationDidReceiveMemoryWarning:(UIApplication *)application;
➡ Partout ailleurs: UIApplicationDidReceiveMemoryWarningNotification
Luc Fayard
« Développement : Alliance d'une science inexacte et d'une activité humaine faillible.»
Crash et ralentissement
Crash et ralentissement
Core Data
➡ Un NSManagedObjectContext ne peux être partagé entre plusieurs threads
➡ Un NSManagedObject n’est valide que dans son NSManagedObjectContext
➡ Conséquence: il ne faut pas passer de NSManagedObject entre plusieurs threads !
Core Data
1
3
2
ObjectID
3
ObjectID
Thread 1 / MOC 1 Thread 2 / MOC 2
[monContext objectWithID:monObjectId]; NSString * monObjectId = [monObjet objectID];
Mais aussi ...
➡ Être très rigoureux sur la gestion des retain / release
➡ User et abuser du respondsToSelector:
➡ Toujours penser aux cas aux limites
➡ Instruments offre des outils pour tracer et comprendre tout ça ...
Confucius
« L'ouvrier qui veut bien faire son travail doit commencer par aiguiser ses instruments. »
Analyser
Analyser
Ce que je voulaisfaire
Ce que je pense avoir fait
Ce que j’ai fait
Analyser
➡ Analyser, c’est comprendre ce qui a été réellement fait
➡ Instruments est l’outil parfait pour ça:
Consommationmémoire
Fuitesmémoire
Consommationd’énergie
Performances graphiques Zombies
Jean-Claude Dusse
« Je sens que, ce soir, je vais conclure »
Conclusions
Conclusions
➡ Bien coder, c’est du temps, de la crédibilité et de l’argent de gagné.
➡ Définir des règles intelligemment ... et s’y tenir !
➡ Soyez courtois avec les ressources
➡ Lisez, creusez et comprenez la documentation ...
julien@cocoaheads.fr
thomas.dupont@cocoaheads.frCocoaHeads #3
Bien débuter sur iOS
Mail : julien@cocoaheads.frWeb : www.cocoaheads.frTwitter : @CocoaHeadsRNS
Bien coder sur iOS
Mail : jeremy.brault@niji.frWeb : www.niji.fr