0

I am making a high score manager.

I load and save as follows:

-(void)load
{
    NSMutableArray* scores = [NSMutableArray arrayWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:HIGH_SCORE_KEY]];

    if(scores == nil)
    {
        highscores = [NSMutableArray array];
    }
    else
    {
        highscores = scores;
    }
}


-(void)save
{
    [[NSUserDefaults standardUserDefaults] setObject:highscores forKey:HIGH_SCORE_KEY];
}

I am storing HighScore objects:

#import <Foundation/Foundation.h>

@interface HighScore : NSObject
{
    int score;
    NSString* name;
}

-(int)score;
-(NSString*)name;
-(id)init;
-(id)initWithName:(NSString*)playerName andScore:(int)playerScore; 
-(BOOL)isEqual:(id)object;
-(BOOL)isLessThan:(id)object;
@end

There is nothing particularly complex about this class. However, when I load, the load does not return nil but returns an empty array, indicating to me that serializing probably failed for some reason.

Any ideas?

Thanks

Vizllx
  • 9,135
  • 1
  • 41
  • 79
jmasterx
  • 52,639
  • 96
  • 311
  • 557

1 Answers1

-1

NSUserDefaults always returns immutable objects, even if the original object was mutable. It's in the documentation for objectForKey:

The returned object is immutable, even if the value you originally set was mutable.

You will need to create a copy of the returned object before you modify it, using [NSMutableArray arrayWithArray:]

Probably also best to use the arrayForKey method of NSUserDefaults if you're retrieving an array. Docs here: http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSUserDefaults_Class/Reference/Reference.html

You can only store objects in NSUserDefaults. So you have to convert the array to an NSArray. However, NSArrays also only store objects, so you need to store the long values encapsulated in an NSNumber object:

//saving
NSUserDefaults *standardDefaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *arrayObj = [[NSMutableArray alloc] init];

long *arr;
arr = new long [10];

for(int i = 0 ; i<10 ; i++) {
    [arrayObj addObject:[NSNumber numberWithLong:arr[i]]];
}

[standardDefaults setObject:arrayObj forKey:@"longArray"];
[arrayObj release];


//reading
NSUserDefaults *standardDefaults = [NSUserDefaults standardUserDefaults];
NSArray *arrayObj = [standardDefaults objectForKey:@"longArray"];

long *arr;
arr = new long [10];

for(int i = 0 ; i<10 ; i++) {
    arr[i] = [(NSNumber*)[arrayObj objectAtIndex:i] longValue];
}
Vizllx
  • 9,135
  • 1
  • 41
  • 79
  • What? `NSUserDefaults` has several methods for storing simple types like `BOOL` and `int`, not just objects. And `NSArray` is an object. Your answer has nothing to do with the problem. The problem is that `NSUserDefaults` only allows a few very specific objects and the OP's custom class isn't one of them. – rmaddy Nov 16 '13 at 06:40
  • Huh? I know what an object is. I'm pointing out mistakes in your answer. The very first sentence you posted is incorrect. `NSUserDefaults` can store a small specific set of object types as well as several non-object types. And your statement that "you have to convert the array to an `NSArray` make no sense. The array already is an `NSArray` (`NSMutableArray` is an `NSArray`). But all of this is a side issue anyway. Your answer has nothing at all to do with the problem as I stated. How does your answer help the OP? How does it help him store his custom objects properly? – rmaddy Nov 16 '13 at 07:25
  • I don't think you are actually reading my comments. The update you made, while true, has nothing to do with the comments I've made. You stated: *"You can only store objects in NSUserDefaults"*. This is false. You can store primitive types too. You stated: *"So you have to convert the array to an NSArray"*. No you don't. The existing `NSMutableArray` is already an `NSArray`. You can store a mutable array. Of course it will be immutable when you read it back. Lastly, your answer doesn't tell the OP how to solve the problem of storing a custom object in `NSUserDefaults`. – rmaddy Nov 16 '13 at 16:09