in Article, Blog Posts, iOS Development

[iOS]How to save and load a custom object?

Here we go.

How to write a custom object which can be archived to a text file?

Usually, we use NSKeyedArchiver to serialize an object and write it to a file.
Correspondingly, NSKeyedUnarchiver is used to get the object from the file.

The NSKeyedArchiver’s interface is like this:

+ (BOOL)archiveRootObject:(id)rootObject toFile:(NSString *)path;

NSKeyedArchiver is a NSCoder. The rootObject should conform to the protocol NSCoding.

@protocol NSCoding

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

@end

For example, now we have a customObject:

@interface CustomObject : NSObject  {
    NSString*   mStringValue;
    int         mIntValue;
    BOOL        mBOOLValue;
}

@property (non-atomic, retain) NSString *stringValue;
@property (nonatomic, assign) int       intValue;
@property (nonatomic, assign) BOOL      boolValue;

@end

It conforms to NSCoding, so we need to implement those two methods in the .m file

#define kEncodeKeyStringValue   @"kEncodeKeyStringValue" 
#define kEncodeKeyIntValue      @"kEncodeKeyIntValue" 
#define kEncodeKeyBOOLValue     @"kEncodeKeyBOOLValue" 

#pragma mark - NSCoding
- (void)encodeWithCoder:(NSCoder *)aCoder {
    [aCoder encodeObject:self.stringValue   forKey:kEncodeKeyStringValue];
    [aCoder encodeInt:self.intValue         forKey:kEncodeKeyIntValue];
    [aCoder encodeBool:self.boolValue       forKey:kEncodeKeyBOOLValue];
}

- (id)initWithCoder:(NSCoder *)aDecoder {
    if ((self = [super init]))
    {
        self.stringValue = [aDecoder decodeObjectForKey:kEncodeKeyStringValue];
        self.intValue    = [aDecoder decodeIntForKey:kEncodeKeyIntValue];
        self.boolValue   = [aDecoder decodeBoolForKey:kEncodeKeyBOOLValue];
    }
    return self;
}

Now, we can save and load a customObject like:

- (IBAction)saveObjectPlain:(id)sender {

    printf("==========================================\n");
    printf("saveObjectPlain===========================\n");
    printf("==========================================\n");

    //< Create and Save the Object
    {
        CustomObject *obj = [[[CustomObject alloc] init] autorelease];
        obj.stringValue = @"The String Value";
        obj.intValue    = 12345;
        obj.boolValue   = YES;
        [NSKeyedArchiver archiveRootObject:obj toFile:[self tempFilePath]];
        printf("Save: \n %s \n", [[obj description] cStringUsingEncoding:NSUTF8StringEncoding]);
    }
    
    //< Load and Print the Object
    {
        CustomObject *obj = [NSKeyedUnarchiver unarchiveObjectWithFile:[self tempFilePath]];
        printf("Load: \n %s \n", [[obj description] cStringUsingEncoding:NSUTF8StringEncoding]);
    }
    
    printf("==========================================\n");
}

Quite easy, right?

How about to save/load an array of our CustomObjects?
Since NSArray conforms to protocol NSCoding, it is quite straightforward.

- (IBAction)saveObjectsInArray:(id)sender {

    printf("==========================================\n");
    printf("saveObjectsInArray========================\n");
    printf("==========================================\n");
    
    //< Create Two Keys and Save the Object
    {
        CustomObject *obj1 = [[[CustomObject alloc] init] autorelease];
        obj1.stringValue = @"The String Value 1";
        obj1.intValue    = 12345;
        obj1.boolValue   = YES;
        
        CustomObject *obj2 = [[[CustomObject alloc] init] autorelease];
        obj2.stringValue = @"The String Value 2";
        obj2.intValue    = 54321;
        obj2.boolValue   = NO;
        
        NSArray *array = [NSArray arrayWithObjects:obj1, obj2, nil];
        [NSKeyedArchiver archiveRootObject:array toFile:[self tempFilePath]];

        printf("Save: \n %s \n ", [[array description] cStringUsingEncoding:NSUTF8StringEncoding]);
    }
    
    //< Load and Print the Object
    {
        NSArray *array = [NSKeyedUnarchiver unarchiveObjectWithFile:[self tempFilePath]];
        printf("Load: \n %s \n ", [[array description] cStringUsingEncoding:NSUTF8StringEncoding]);
    }
    
    printf("==========================================\n");
}

So, if there is a member variable of type NSArray in our CustomObject,
we need to make sure the array contains only objects conform to NSCoding.
Otherwise, an exception of unrecognized selector [.. encodeWithCoder:] will be raised.

One step further, how to save/load a NSDictionary has a CustomObject as the key and another CustomObject as the Object.

This is actually another problem. We need to make sure CustomObject can be used as a key in the NSDictionary.
According to the documentation, NSDictionary requires its key to conform to the protocol NSCopying.

@protocol NSCopying

- (id)copyWithZone:(NSZone *)zone;

@end

The key object needs to be copied to a specified piece of memory described by NSZone.
The implementation is like:

#pragma mark - NSCopying
- (id)copyWithZone:(NSZone *)zone {
    CustomObject *copy = [[CustomObject allocWithZone:zone] init];
    copy.stringValue = [[self.stringValue copy] autorelease];
    copy.intValue = self.intValue;
    copy.boolValue = self.boolValue;
    return copy;
}

We use allocWithZone to allocate a CustomObject on the indicated piece of memory.
Then copy member variables to the new instance.

The save/load procedure is like:

- (IBAction)saveObjectAsKey:(id)sender {

    printf("==========================================\n");
    printf("saveObjectAsKey===========================\n");
    printf("==========================================\n");
    
    //< Create Two Keys and Save the Object
    {
        CustomObject *keyObj = [[[CustomObject alloc] init] autorelease];
        keyObj.stringValue = @"The String Value Key";
        keyObj.intValue    = 12345;
        keyObj.boolValue   = YES;

        CustomObject *valObj = [[[CustomObject alloc] init] autorelease];
        valObj.stringValue = @"The String Value Value";
        valObj.intValue    = 54321;
        valObj.boolValue   = NO;
        
        NSDictionary *dict = [NSDictionary dictionaryWithObject:valObj forKey:keyObj];
        [NSKeyedArchiver archiveRootObject:dict toFile:[self tempFilePath]];

        printf("Save: \n %s \n", [[dict description] cStringUsingEncoding:NSUTF8StringEncoding]);
    }
    
    //< Load and Print the Object
    {
        NSDictionary *dict = [NSKeyedUnarchiver unarchiveObjectWithFile:[self tempFilePath]];
        printf("Load: \n %s \n", [[dict description] cStringUsingEncoding:NSUTF8StringEncoding]);
    }
    
    printf("==========================================\n");
}

The whole demo can be found here:
Demo CustomObject NSCoding

Hope it helps! :]

----------------------------------------------------
When we were learning new things, we searched for answers in Google, from Stackoverflow and followed well-written tutorials. When other one faces problems we met and solved, we are glad to answer their questions, post tutorial for the related topics. This is called the spirit of Community. I love it.

Leave a Reply for Sigma Cancel Reply

Write a Comment

Comment

12 Comments

  1. When we were learning new things, we searched for answers in Google, from Stackoverflow and followed well-written tutorials. When other one faces problems we met and solved, we are glad to answer their questions, post tutorial for the related topics. This is called the spirit of Community. I love it.==========================
    赞这段话,话说我打出来的字直接看不多,和留言版的背景都是白色。。。不太方便啊

  2. 那个文字是白色的原因貌似是因为我前面考了一段你文章的白色文字,导致字体变白了,之后手写的也是白色看不见。。。
    直接在回复中打字的话是黑色的

  3. Great article. It really helped understand the things involved. Thanks for a great effort.

  4. Brilliant post, the 4 tutorials I tried before this didn’t make sense because they missed the ‘saveObjectAsKey’ method – finally this totally nailed it!

  5. Great thanks.
    damn happy for this.
    i have faced issue in that i want to store the whole objects ( Control Objects as well ) in file i have tried hard to solve but i haven’t got any help from any one or from any other example.

    Above example have solved my issue. 🙂

  6. #import

    @interface Person : NSObject{

    NSString *mName;

    NSString *mSex;

    }

    @property(retain,nonatomic) NSString *name,*sex;

    @end

    #import “Person.h”

    #define nameString @”name”

    #define sexString @”sex”

    @implementation Person

    @synthesize name,sex;

    -(void) encodeWithCoder:(NSCoder *)aCoder{

    [aCoder encodeObject:self.name forKey:nameString];

    [aCoder encodeObject:self.sex forKey:sexString];

    }

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

    self = [super init];

    if(self){

    self.name = [aDecoder decodeObjectForKey:nameString];

    self.sex = [aDecoder decodeObjectForKey:sexString];

    }

    return self;

    }

    @end

    Person *p = [[Person alloc] init];

    p.name = @”lq”;

    p.sex = @”mail”;

    if([NSKeyedArchiver archiveRootObject:p toFile:@”fffff”]){

    Person *p2 = [NSKeyedUnarchiver unarchiveObjectWithFile:@”fffff”];

    NSLog(@”%@–%@”,p2.name,p2.sex);

    }else{

    NSLog(@”failed”);

    }

    It didn’t work,why?

  7. Thanks for this nice post Li. But what about if i have a array in same class which hold other customobjets. Can you explain to solve this case.

  8. When I initially commented I clicked the “Notify me when new comments are added” checkbox and now each
    time a comment is added I get several e-mails with the same comment.
    Is there any way you can remove me from that service?
    Appreciate it!

Webmentions

  • How to save and load a custom object » iFun5 2023/07/25

    […] 转自:http://haoxiang.org/2011/11/ios-how-to-save-and-load-a-custom-object/ […]