Jinglei.Y Jinglei.Y - 27 days ago 8
iOS Question

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

Answer

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.