Aller au menu - Aller au contenu

Icône Données sans ordre

Mise à jour : 07/04/2011
Difficulté : Intermédiaire Intermédiaire Creative Commons BY-NC-SA
2 178 visites depuis 7 jours, dont 22 sur ce chapitre classé 66/786
Dans le chapitre précédent, nous avons vu comment stocker des objets de manière organisée, suivant un certain ordre (déterminé par l'index). Ce n'est pas le seul moyen de stocker des données avec Cocoa. Il existe d'ailleurs deux objets qui permettent de stocker des données sans ordre précis : NSDictionary et NSSet.

L'objet NSDictionary a un fonctionnement particulier. Les objets ne sont plus classés dans un certain ordre, mais sont associés à des objets spéciaux : des clés. Il n'y a donc plus d'ordre véritable. Un objet NSDictionary est communément appelé dictionnaire.

L'objet NSSet va encore plus loin : il se débarrasse de tout ordre. o_O Les objets ne sont alors conformes à aucun arrangement à l'intérieur du NSSet. Un objet NSSet est communément appelé ensemble (comme un ensemble mathématique).

Votre intérêt pour ces objets inconnus étant soudainement apparu, je vous propose de lire la suite ! :p
Sommaire du chapitre :
Icône du chapitre
Chapitre précédent Sommaire Chapitre suivant

Principe du dictionnaire

Il y a tout d'abord deux choses qu'il convient de distinguer dans un dictionnaire : les clés et les objets contenus dans le dictionnaire. Les clés sont des objets de type id. On peut donc utiliser n'importe quel objet en tant que clé dans un NSDictionary. C'est très pratique, bien que souvent, on se contentera d'utiliser des objets NSString.

Dans un dictionnaire, il y a une association entre une clé et un objet, de la même façon qu'il y avait une association entre un index et un objet dans le NSArray. Pour retrouver un objet stocké dans un dictionnaire, il suffit de trouver sa clé.

Ce fonctionnement peut être schématisé comme ceci :

Image utilisateur


Tout comme l'objet NSArray, le NSDictionary peut contenir n'importe quel type d'objet (NSString, NSNumber, etc.).

Quel est l'intérêt de cet objet par rapport à une structure C classique ? On peut tout aussi bien faire ça avec une structure, n'est-ce pas ?

C'est vrai, on pourrait créer une structure Personne qui contiendrait une adresse et un numéro de téléphone. Mais l'avantage du NSDictionary réside dans sa souplesse : on peut créer un NSDictionary de la forme que l'on veut ; il n'y a pas besoin de définir les clés comme on le ferait avec les sous-variables (champs) d'une structure C. ;)

Cette souplesse pose quand même un problème : comment savoir si une clé existe dans un dictionnaire ? Mais avant de répondre à cette question, voyons plutôt comment créer des NSDictionary. :-°

Manipuler un dictionnaire

La création d'une instance de la classe NSDictionary est très similaire à celle d'une instance de la classe NSArray. On retrouve la même forme pour les méthodes d'initialisation :

Code : Objective-C
1
NSDictionary * dict = [NSDictionary dictionaryWithObject:@"Genève" forKey:@"Ville"];

Dans cet exemple, on initialise un dictionnaire avec un seul couple clé-objet. La clé est une chaîne : @"Ville" ; l'objet est également une chaîne : @"Genève". Notez que le prototype de la méthode de classe définit les paramètres comme étant des objets de type id, c'est-à-dire n'importe quels objets.

Et si on veut créer plusieurs couples dans un même dictionnaire, c'est possible ?

Bien sûr, mais il faut connaître les clés et les objets que l'on va fournir au dictionnaire. On doit créer deux tableaux qui vont contenir les objets et les clés, et ces tableaux seront passés en paramètre au dictionnaire.

Code : Objective-C
1
2
3
4
5
NSArray * lesObjets = [NSArray arrayWithObjects:@"Genève", @"Suisse", @"Europe", nil];
NSArray * lesCles = [NSArray arrayWithObjects:@"Ville", @"Pays", @"Continent", nil];


NSDictionary * dict = [NSDictionary dictionaryWithObjects:lesObjets forKeys:lesCles];

Attention : ici, l'ordre des objets et des clés est très important. Il faut que l'objet d'indice i soit associé à la clé d'indice i ! ;)

Comme l'initialisation d'un dictionnaire avec plusieurs paires clé-objet est une opération courante et que passer par des tableaux est un peu lourd, les développeurs ont construit une autre méthode de classe.

Code : Objective-C
1
2
3
NSDictionary * dict = [NSDictionary dictionaryWithObjectsAndKeys:@"Genève", @"Ville",
                                                                 @"Suisse", @"Pays",
                                                                 @"Europe", @"Continent", nil];

Comme vous pouvez le constater, on n'a plus besoin de tableau ici. En revanche, il faut faire attention aux paires : on donne toujours l'objet en premier, puis la clé qui lui est associée, et on termine la liste par nil.

Bien entendu, toutes ces méthodes de classe ont leur analogue avec init... : - initWithObject:forKey:, - initWithObjectsAndKeys:, etc. :) Il faut bien sûr se soucier de la mémoire dès lors qu'on utilise l'une de ces méthodes.


Manipuler les couples


Pour accéder au contenu d'un dictionnaire, on utilise la méthode - objectForKey:, qui prend en paramètre la clé associée à l'objet. Par exemple, ce code :

Code : Objective-C
1
2
3
4
5
NSDictionary * dict = [NSDictionary dictionaryWithObjectsAndKeys:@"Genève", @"Ville",
                                                                 @"Suisse", @"Pays",
                                                                 @"Europe", @"Continent", nil];

NSLog (@"Vous habitez en %@, à %@ !", [dict objectForKey:@"Pays"], [dict objectForKey:@"Ville"]);

... va produire l'affichage suivant :

Code : Console
Vous habitez en Suisse, à Genève !

Facile, non ? ^^

On peut même afficher un dictionnaire grâce à la fonction NSLog(). Voici la description de l'objet dict (cf. le TP de la partie I).

Code : Console
{
    Ville = Genève;
    Pays = Suisse;
    Continent = Europe;
}


Changer les couples


Tout comme une instance de NSArray, une instance de NSDictionary est immuable. Pour pouvoir modifier un dictionnaire, il faut une instance de la classe NSMutableDictionary. On peut alors modifier les couples clé-objet :

Code : Objective-C
1
[dict setObject:nouvelObjet forKey:@"Clé"];

On a deux cas de figure ici :
  • soit le couple ayant pour clé l'objet @"Clé" existe déjà : à ce moment-là, l'ancien objet reçoit le message - release, tandis que le nouvel objet prend sa place dans le dictionnaire et reçoit le message - retain ;
  • soit le couple n'existe pas encore : il est alors créé. ;)

Bien entendu, on peut aussi supprimer des objets grâce à la méthode - removeObjectForKey:. Dans ce cas-ci, si la clé n'existe pas, il ne se passe rien. On peut également utiliser la méthode - removeAllObjects qui, comme son nom l'indique, vide le dictionnaire.

Autres méthodes


Comme avec NSArray, la classe NSDictionary peut fournir des NSEnumerator. En fait, elle peut en fournir deux : un pour les clés et un autre pour les objets. C'est très pratique pour parcourir un dictionnaire :

Code : Objective-C
1
2
3
4
5
6
7
/* On reprend le dictionnaire "dict" qui contient la ville "Genève" */
NSEnumerator * cleEnum = [dict keyEnumerator];
id cle = nil;

while (cle = [cleEnum nextObject]) {
    NSLog (@"Clé : %@\nValeur : %@\n\n", cle, [dict objectForKey:cle]);
}

Code : Console
Clé : Ville
Valeur : Genève

Clé : Pays
Valeur : Suisse

Clé : Continent
Valeur : Europe

Rien de bien sorcier, comme vous pouvez le constater.

On peut également récupérer les clés sous la forme d'un tableau grâce à la méthode - allKeys, ou encore récupérer les objets grâce à - allValues.

Si vous jetez un coup d'?il à la documentation, vous pouvez remarquer que quelques méthodes s'écrivent avec le nom object et que d'autres s'écrivent avec value (on a par exemple - objectForKey: et - valueForKey:). À notre niveau, il n'y a pas de différence entre ces méthodes. Plus tard, nous verrons une nouvelle spécificité de l'Objective-C, nommée KVC, qui expliquera la différence entre ces deux familles de méthodes. ;)

Enfin, pour connaître le nombre de couples présents dans le dictionnaire, il suffit d'appeler la méthode - count qui renvoie un int. :)

Je vous laisse consulter la documentation de NSDictionary si vous souhaitez approfondir vos connaissances de cette classe.

Les ensembles

Les ensembles sont des objets beaucoup moins communs que les tableaux ou les dictionnaires. Cependant, il est fort probable que vous en rencontriez un au cours de votre développement avec Cocoa. C'est pourquoi je vais en parler rapidement.

Les objets NSSet sont utiles lorsque vous avez besoin de stocker des objets sans ordre particulier, par opposition au NSArray. Le fait que les objets soient stockés sans ordre précis restreint l'usage d'un ensemble à un but précis : déterminer si un objet est présent dans l'ensemble.

On peut faire ça avec un tableau, en exécutant la méthode - containsObject:. Cependant, cette méthode est plutôt lente et gourmande en ressources, car il faut parcourir tout le tableau pour trouver l'objet (dans le pire des cas). Un ensemble est plus efficace à ce petit jeu, car il utilise un algorithme de hachage pour différencier les objets et les retrouver. Notez qu'un dictionnaire utilise également le hachage pour organiser ses objets (en interne bien sûr, nous ne voyons pas cette organisation lorsque nous utilisons un dictionnaire).

Créer un ensemble


Vous devez maintenant en avoir l'habitude ; on utilise souvent les mêmes méthodes, avec quelques variantes :

Code : Objective-C
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
/* Initialisation avec un seul objet */
NSSet * set = [NSSet setWithObject:[NSNumber numberWithInt:2]];

/* Initialisation avec un tableau */
NSSet * set = [NSSet setWithArray:mesObjetsRanges];

/* Initialisation avec plusieurs objets */
NSSet * equipe = [[NSSet alloc] initWithObjects:@"Validateurs",
                                                @"zCorrecteurs",
                                                @"Newsers",
                                                @"Modérateurs",
                                                @"Administrateurs", nil];
// Attention à la mémoire !
[equipe release];

Bien sûr, la méthode + setWithObjects: existe aussi. :)

Se renseigner sur les objets


Pour savoir si un objet appartient à un ensemble, on exécute la méthode - containsObject:. Cette méthode retourne un booléen qui vaut YES si l'objet dépend de l'ensemble, et NO dans le cas contraire.

Code : Objective-C
1
2
3
if ( [equipe containsObject:@"Validateurs"] ) {
    NSLog (@"Les validateurs font partie de l'équipe !");
}

La méthode - member: est légèrement différente. Elle vérifie aussi si l'objet passé en paramètre appartient à l'ensemble. Si c'est le cas, elle retourne l'objet trouvé. Sinon, elle retourne nil.

On peut récupérer les objets contenus dans un ensemble de plusieurs façons.
  • La méthode - allObjects retourne un tableau contenant tous les objets de l'ensemble. Attention cependant, l'ordre du tableau n'est pas défini : on ne sait pas quel objet sera à l'indice i.
  • La méthode - objectEnumerator retourne un objet NSEnumerator, fonctionnant de la même façon que tous ceux que l'on a vus jusqu'à présent. De même, l'ordre n'est pas défini.
  • La méthode - anyObject retourne un objet de l'ensemble. Attention, l'objet n'est pas forcément tiré au sort, ni toujours le même.


Classes filles


Tout comme les classes que l'on a vues précédemment, NSSet possède une classe fille NSMutableSet, qui permet d'ajouter des objets (- addObject:) ou d'en enlever (-removeObject:).

Toutefois, contrairement aux autres classes étudiées jusqu'ici, NSMutableSet possède une classe fille : NSCountedSet. Cette classe permet de compter le nombre de fois qu'un objet apparaît dans un ensemble. Avec les classes NSSet et NSMutableSet, un objet ne peut apparaître qu'une seule fois dans un ensemble, même si on l'y ajoute plusieurs fois. NSCountedSet associe un compteur à chaque objet présent dans l'ensemble et l'incrémente à chaque fois qu'un objet est ajouté. ;)

Q.C.M.

Quel genre d'algorithme utilisent les dictionnaire et les ensembles pour « retrouver » leurs objets ?
Quelle sera la valeur de objet ?

Code : Objective-C
1
2
id dict = [NSDictionary dictionaryWithObjectsAndKeys:@"Ville", @"Lausanne", @"Rue", @"Chemin du Bourg", nil];
id objet = [dict objectForKey:@"Rue"];

Statistiques de réponses au QCM

Nous venons d'apprendre les trois grands moyens de stocker des données avec le framework Foundation. Ces chapitres étaient nécessaires pour passer à des choses plus complexes, comme la manipulation des fichiers ! :D

Liens vers la doc


Chapitre précédent Sommaire Chapitre suivant

Partager

Il n'y a pas encore de commentaire pour ce tuto.

Ce tutoriel a été corrigé par les zCorrecteurs.