Search code examples
iosobjective-cnsarraynsuserdefaults

iOS - An NSArray of a single NSData object saved in NSUserDefault come out to be NSData?


I'm saving a customized class DeviceInfo in to NSDefault.

I already used NSKeyedArchiver to archive the object into a NSData object.

As I want to save an array of deviceInfo objects into NSUserDefault, I put the archived objects into an NSArray.

However, it seems that, when there's only one NSData object in the array, I always get an NSData object while it should be an NSArray object when I fetch it from NSUserDefault.

Why is that?

Here's the code.

NSData* data = [NSKeyedArchiver archivedDataWithRootObject:_deviceInfo];

NSMutableArray *temp = [NSMutableArray new];
temp = [[SDGridItemCacheTool itemsArray] mutableCopy];    //[[SDGridItemCacheTool itemsArray]] is the method the fetch the array, I paste the code below.
[temp addObject:data];
[SDGridItemCacheTool saveItemsArray:[temp copy]];

And the code to save and fetch data from NSUserDefault

//SDGridItemCacheTool.m
+ (NSArray *)itemsArray
{
    return [[NSUserDefaults standardUserDefaults] objectForKey:kItemsArrayCacheKey];
}

+ (void)saveItemsArray:(NSArray *)array
{
    [[NSUserDefaults standardUserDefaults] setObject:[array copy] forKey:kItemsArrayCacheKey];
    [[NSUserDefaults standardUserDefaults] synchronize];
}

Finally the DeviceInfo class:

@interface DeviceInfo : NSObject <NSCoding>

@property (nonatomic, retain) NSString* title;
@property (nonatomic, retain) NSString* imageResString;
@property (nonatomic, retain) NSNumber* currentStat;
@property (nonatomic, retain) NSDictionary* colorStatPair;

@end

@implementation DeviceInfo

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    if (self = [super init]) {
        _title = [aDecoder decodeObjectForKey:kUserDefaultDeviceTitleKey];
        _imageResString = [aDecoder decodeObjectForKey:kUserDefaultDeviceImageResStringKey];
        _currentStat = [aDecoder decodeObjectForKey:KUserDefaultDeviceCurrentStatKey];
        _colorStatPair = [aDecoder decodeObjectForKey:kUserDefaultDeviceColorStatPair];
    }
    return self;
}

-(void)encodeWithCoder:(NSCoder *)aCoder {
    [aCoder encodeObject:_title forKey:kUserDefaultDeviceTitleKey];
    [aCoder encodeObject:_imageResString forKey:kUserDefaultDeviceImageResStringKey];
    [aCoder encodeObject:_currentStat forKey:KUserDefaultDeviceCurrentStatKey];
    [aCoder encodeObject:_colorStatPair forKey:kUserDefaultDeviceColorStatPair];
}

@end

Solution

  • There are a few issues with your code.

    Your itemsArray method will return nil until you actually save some data. So you need to handle that properly. Your saveItemsArray: method needlessly does a copy of the array and it needlessly calls synchronize.

    Updated SDGridItemCacheTool.m

    + (NSArray *)itemsArray
    {
        NSArray *result = [[NSUserDefaults standardUserDefaults] objectForKey:kItemsArrayCacheKey];
        if (!result) {
            result = [NSArray array];
        }
    
        return result;
    }
    
    + (void)saveItemsArray:(NSArray *)array
    {
        [[NSUserDefaults standardUserDefaults] setObject:array forKey:kItemsArrayCacheKey];
    }
    

    And your code that calls these two methods needs some cleanup.

    NSData* data = [NSKeyedArchiver archivedDataWithRootObject:_deviceInfo];
    
    NSMutableArray *temp = [[SDGridItemCacheTool itemsArray] mutableCopy];
    [temp addObject:data];
    [SDGridItemCacheTool saveItemsArray:temp];
    

    There was no need to create an NSMutableArray and then replace it. And there's no need to copy the array.

    You may also wish to delete and reinstall your app to start with clean user defaults. If you have changed this code a bit, you may have a non-array stored in that key from earlier code.