0

I have this class with an NSMutableArray of custom Cocos2d objects implementing the NSCoding protocol.

@interface PlayerData : NSObject <NSCoding> {
}

@property (readwrite, nonatomic) NSMutableArray *levelsStars; //Where levelsStars is filled with objects conforming to the NSCoding protocol.

The objects are of type LevelData, which inherits from CCNode (a Cocos2d class) and conforms to the NSCoding protocol.

@interface LevelData : CCNode <NSCoding>

Here is the implementation of the protocol in PlayerData. In order to encode and decode the NSMutableArray of custom Cocos2d objects, which conform to NSCoding objects:

-(void) encodeWithCoder:(NSCoder *)aCoder
{
    [aCoder encodeObject:[NSKeyedArchiver archivedDataWithRootObject:levelsStars] forKey:kLevelsStars];
}


-(id) initWithCoder:(NSCoder *)aDecoder
{
    self = [super init];
    if (self) {
        //Reading arrays:
        NSData * dataRepresentingLevelStars = [aDecoder decodeObjectForKey:kLevelsStars];
        if (dataRepresentingLevelStars!=nil) {
            NSArray * oldSavedArray = [NSKeyedUnarchiver unarchiveObjectWithData:dataRepresentingLevelStars];
            if (dataRepresentingLevelStars!=nil) {
                levelsStars = [NSMutableArray arrayWithArray:oldSavedArray];
            }
            else
            {
                levelsStars = [[NSMutableArray array] initWithCapacity:10];
            }
        }

    }

    return self;
}

Is this approach correct? I based it on this question / answer.

EDIT: I thought I will add some more details on my use case

My users can choose among different characters, each characters corresponds to a PlayerData object which I plan to store in a different file appending the user's game center id (id-character1.archive, id-character2.archive etc..).

I do save progress of the characters in the object (e.g. score, life) including the NSMutableArray custom array of Cocos2d-objects (in my real case I do have 5 different arrays containing 20/30 objects each).

Community
  • 1
  • 1
mm24
  • 9,280
  • 12
  • 75
  • 170
  • Please refer to answer of this question. I hope this will answer your question. http://stackoverflow.com/questions/2315948/how-to-store-custom-objects-in-nsuserdefaults – Vaibhav Gautam Aug 22 '13 at 13:42
  • Thanks but I do not want to use NSUserDefaults and hence I am asking if my understanding and implementation of the solution I linked is correct.. – mm24 Aug 22 '13 at 13:44
  • yes it is correct but i would recommend you to use it only for small data otherwise it is very slow. – Vaibhav Gautam Aug 22 '13 at 13:48
  • Thanks for the suggestion, why is it very small? My use case is: my users can choose among different characters, each characters corresponds to a PlayerData object and I do save progress of the characters in the object (e.g. score, life) including the NSMutableArray custom array of Cocos2d-objects (in my real case I do have 5 different arrays containing 20/30 objects each). – mm24 Aug 22 '13 at 13:53
  • 30 objects in 5 different arrays = 150 objects. coding and decoding in NSData is slow. I would reccomend you to store it in seperate plist file instead of coding or decoding, That will increase your coding efforts but will improve performance of app. – Vaibhav Gautam Aug 22 '13 at 13:57
  • I will test it and then use the plist if doesn't perform properly. Thanks – mm24 Aug 22 '13 at 14:00

1 Answers1

0

In your main init method for the PlayerData object you want to do the unarchive:

- (id)init {
    self = [super init];
    if (self) {
        self.levelsStars = [NSKeyedUnarchiver unarchiveObjectWithFile:pathToArchive];
        if (self.levelsStars == nil) self.levelsStars = [NSMutableArray array];
    }
}

This will then call initWithCoder: on each of your objects that was in the array. And pull them back into a mutable array.

You will want to have some method for saving your data though, again on your PlayerData object:

- (void)applicationDidEnterBackground:(NSNotification *)notification {
    [NSKeyedArchiver archiveRootObject:self.levelsStars toFile:pathToArchive];
}

This will call encodeWithCoder: on each of the objects in the array and write them out to a file.

George Green
  • 4,807
  • 5
  • 31
  • 45
  • From my understanding the archive and unarchive object with data works only with NSData objects and, given I have a NSMutable array with custom objects I have to encode this in a NSData object first (as in my solution). Is there something wrong in my solution? – mm24 Aug 22 '13 at 13:57
  • Is your playerData object your highest level object, or is it archived out by another class? – George Green Aug 23 '13 at 12:04
  • In the comments someone has said that your way works fine, so maybe stick with it, it just seems overly complicated to me... – George Green Aug 23 '13 at 12:05
  • there is static manager class that loads and unloads PlayerData according to the character being currently selected. Is there somewhere a good design pattern reference/tutorial for this type of problems as far as u know? – mm24 Aug 24 '13 at 11:28
  • So the static manager should be the only one calling to NSKeyedArchiver or NSKeyedUnarchiver to archive either the player or an array of players. Then the custom player class should just implement NSCoding along with the custom playerStars class. – George Green Aug 27 '13 at 13:35