1.9 Rendre un objet serializable – NSCoding

NSCoding

NSCoding où comment sérialiser en Objective-C

Une pratique très utile et classique dans le monde de l’objet : rendre ses objets sérialisables.

En Objective-C il existe un protocole NSCoding pour la sérialisation. D’autres langages comme Java offrent le même mécanisme : l’interface java.io.Serializable par exemple.

Voici ci-dessous un exemple de mise en oeuvre en Objective-C :

Soit la classe suivante en Objective-C :

@interface LTMPoint

@property( nonatomic ) CGFloat X;
@property( nonatomic ) CGFloat Y;

+(LTMPoint*) createPoint;

@end

Cette classe propose deux propriétés simples : X et Y, plus une méthode statique de création d’un LTMPoint.

Rendre une classe Objective-C sérialisable

Comment rendre cette classe serializable ?
Il suffit pour notre classe d’adhérer au protocole NSCoding.

@interface LTMPoint : NSObject <NSCoding>

@property( nonatomic ) CGFloat X;
@property( nonatomic ) CGFloat Y;

+(LTMPoint*) createPoint;

-(void)encodeWithCoder:(NSCoder *)aCoder;
-(id)initWithCoder:(NSCoder *)aDecoder;

@end

Ci-dessus la classe adhère désormais au protocole (interface au sens POO) NSCoding et donc implémente :
1. Le constructeur surchargé initWithCoder:
Celui-ci permettra au mécanisme de sérialisation de « dé sérialiser » l’objet : c’est à dire de relire et re constituer son état à partir d’un fichier sauvegardé sur disque (voir en fin d’article comment faire).

2. Le message encodeWithCoder:
Il permet l’opération inverse : c’est à dire d’écrire l’état de l’objet sur disque.

Implémentation de la sérialisation

Ici le fichier d’implémentation

// for writing
-(void)encodeWithCoder:(NSCoder *)aCoder {
    [aCoder encodeFloat:_X forKey:@"X"];
    [aCoder encodeFloat:_Y forKey:@"Y"];
}

// for reading
-(id)initWithCoder:(NSCoder *)aDecoder {
    _X = [aDecoder decodeFloatForKey:@"X"];
    _Y = [aDecoder decodeFloatForKey:@"Y"];
    return [self init];
}

Ci-dessous les attributs X et Y sont encodés et décodés par ces méthodes standards. On précisera donc ici les champs non transitoires, candidats à la sauvegarde de leurs états.

Allons plus loin

Nous rajoutons ici une nouvelle classe Objective-C appelée : LTMSegment. Cette dernière contient 2 instances de LTMPoint.
Nous souhaitons donc rendre sérialisable le segment qui contiendra lui-même deux points sérialisables.

Voici le code :

Fichier header :

@interface LTMSegment : NSObject <NSCoding>

@property (nonatomic) LTMPoint * p1;
@property (nonatomic) LTMPoint * p2;

-(id) initWithCoder:(NSCoder *)aDecoder;
-(void) encodeWithCoder:(NSCoder *)aCoder;

@end

Implémentation :

@implementation LTMSegment

@synthesize p1 = _p1;
@synthesize p2 = _p2;

-(id) initWithCoder:(NSCoder *)aDecoder {

    _p1 = [aDecoder decodeObjectForKey:@"p1"];
    _p2 = [aDecoder decodeObjectForKey:@"p2"];
    return [self init];
}

-(void) encodeWithCoder:(NSCoder *)aCoder {
    [aCoder encodeObject:_p1 forKey:@"p1"];
    [aCoder encodeObject:_p2 forKey:@"p2"];
}

@end

A chaque sérialisation d’un LTMSegment, les deux instances contenus seront automatiquement sérialisées.

Utiliser la sérialisation

Pour utiliser la sérialisation il nous faudra préciser un chemin et un fichier sur disque et utiliser les fonctions en vigueur pour ce faire.

Création d’un fichier sur disque

NSMutableString * pPath = [NSMutableString stringWithString:NSHomeDirectory()];
[pPath appendFormat:@"/Documents/persitent.file"];

Ci-dessus on crée une string représentant le chemin et le fichier.

Création d’un instance de LTM Segment

LTMSegment * pSegment = [[LTMSegment alloc] init];
pSegment.p1.X = 1.;
pSegment.p1.Y = 3.;
pSegment.p2.X = 2.;
pSegment.p2.Y = 4.;

Ci-dessus code standard pour créer une instance de segment.

Sérialisation des objets sur disque

BOOL ret;
NSData * pData = [NSKeyedArchiver archivedDataWithRootObject:pSegment];
ret = [pData writeToFile:pPath atomically:YES];

if( ret == NO ) {
    NSLog(@"write -> false");
    return;
}

Ci-dessus on utilise la classe NSKeyedArchiver et le message archivedDataWithRootObject: pour archiver dans un NSData les objets à sérialiser, puis on sauvegarde sur disque ce flux.

Dé sérialisation des objets sur disque

LTMSegment * pSegment2 = nil;

pSegment2 = [NSKeyedUnarchiver unarchiveObjectWithFile:pPath];

if( pSegment2 != nil ) {
    NSLog(@"segment p1.y = %f", pSegment2.p1.Y );
}

Ci-dessus le code de dé sérialisation des objets (le segment et ses deux points).

En résumé nous avons :
1. Rendu la classe LTMPoint sérialisable
2. Rendu la classe LTMSegment sérialisable
3. Créer une instance de LTMSegment
4. Sauvegarde le segment via la sérialisation
5. Re créer une instance du type LTMSegment à partir du fichier disque.

Si cet article vous a plu, vous pouvez partager celui-ci en cliquant sur les boutons ci-dessous.

Catégories
0 Comments
0 Pings & Trackbacks

Laisser un commentaire