取消归档返回nil和不正确格式的编码数据

mas*_*ers 4 nscoding nskeyedarchiver ios nskeyedunarchiver

我已将类别方法添加到NSUserDefaults中以存储和检索编码的对象(在本例中为NSArray)。我在检索编码数据时遇到问题。这是我的代码:

- (void)encodeObject:(id<NSCoding>)object forKey:(NSString *)key {
    NSData *data = [NSKeyedArchiver archivedDataWithRootObject:object requiringSecureCoding:NO error:nil];
    [self setObject:data forKey:key];
    [self synchronize];
}

- (id)decodeObjectForKey:(NSString *)key class:(Class)aClass {
    NSData *data = [self objectForKey:key];
    NSError *error = nil;
    id object = [NSKeyedUnarchiver unarchivedObjectOfClass:aClass fromData:data error:&error];
    NSLog(@"E: %@ %@", error.localizedDescription, object);
    return object;
}
Run Code Online (Sandbox Code Playgroud)

调用[[NSUserDefaults standardDefaults] encodeObject:object forKey:key]应对传递的对象进行编码并将其存储为默认值,然后调用[[NSUserDefaults standardDefaults] decodeObjectForKey:key class:aClass应返回编码的对象。

问题是[NSKeyedUnarchiver unarchivedObjectOfClass:fromData:error:]返回nil,错误文本记录为The data couldn’t be read because it isn’t in the correct format.。使用检索的数据[self objectForKey:]类型为__NSCFData。我不知道这是否相关,因为AFAIK __NSCFData是免费电话桥接到NSData。

替换[NSKeyedUnarchiver unarchivedObjectOfClass:fromData:error:][NSKeyedUnarchiver unarchiveObjectWithData:]可解决问题。数据已正确存储和检索。但是现在不赞成使用此方法,因此我需要转到更现代的方法,但无法确定为什么它不起作用。

CMa*_*ash 8

对于自定义类(或使其足够清晰),其他答案都没有足够好地涵盖这一点,因此希望我可以通过添加答案来帮助某些人!

如果您的数据表示具有各种类属性的自定义类的存档数组,那么仅提供数组类和自定义类是不够的,您需要提供自定义类使用的所有类,例如:

NSData* data;
NSError* error;
[NSKeyedUnarchiver unarchivedObjectOfClasses:
                        [NSSet setWithArray: @[
                             [NSMutableArray class],
                             [CustomClass class],
                             // CustomClass has properties using the following classes:
                             [NSDate class],
                             [NSString class],
                             [NSNumber class],
                             [NSIndexSet class]
                         ]]
                         fromData: data
                         error: &error];
Run Code Online (Sandbox Code Playgroud)

  • 我认为您不需要指定“CustomClass”类的属性类。我发现当解压包含自定义类元素的“NSArrray”或“NSMutableArray”时,我只需要创建一个包含“NSArray”(或“NSMutableArray”)和我的自定义类的集合。 (2认同)

Phi*_*ell 5

我有一个类似的问题。我发现我必须传递NSArray中对象的类。

NSSet *set = [NSSet setWithArray:@[
                      [NSArray class],
                      [STUFF_IN_ARRAY class]
                      ]];

NSArray *results = [NSKeyedUnarchiver unarchivedObjectOfClasses:set fromData: rawData error: &error];
Run Code Online (Sandbox Code Playgroud)


Mar*_*uch 5

为了获得灵感,我正在通过 [NSKeyedUnarchiver unarchivedObjectOfClasses: fromData:] 处理从文件中解压缩数据的方法... 有一个小的改进:您不需要在方法调用中列出所有类,因为最常用的类是自动添加的在方法体中。

// MyFileManager.m
- (id)getDataFromFile:(NSString *)file inSubfolder:(NSString *)subfolder dataClasses:(NSArray<Class> *)classes {
    NSString *filePath = [self pathForFile:file subfolder:subfolder];

    NSData *data = [[NSFileManager defaultManager] contentsAtPath:filePath];
    NSArray *extendedClasses = [classes arrayByAddingObjectsFromArray:@[[NSArray class], [NSMutableArray class], [NSDictionary class], [NSMutableDictionary class], [NSDate class], [NSNumber class]]];

    NSError *unarchivingError;
    id unarchived = [NSKeyedUnarchiver unarchivedObjectOfClasses:[NSSet setWithArray:extendedClasses] fromData:data error:&unarchivingError];

    if (unarchivingError) {
        return nil;
    } else {
        return unarchived;
    }
}
Run Code Online (Sandbox Code Playgroud)

这是该方法的用法 // 预期的未归档数据在字典中

NSDictionary *dictionary = [FILE_MANAGER getDataFromFile:@"testfile" inSubfolder:nil dataClasses:@[[MyClass class]]];
Run Code Online (Sandbox Code Playgroud)

还有一个小建议。您的“MyClass”必须符合“NSSecureCoding”协议。并在你的“MyClass”中实现 3 个方法......

@interface MyClass : NSObject <NSSecureCoding> // MyClass.h

@property (nonatomic, copy) NSString *name;
@property (nonatomic, strong) NSDate *date;
@property (nonatomic) NSInteger count;

@end

@implementation MyClass // MyClass.m

- (id)initWithCoder:(NSCoder *)decoder {
    if (self = [super init]) {
        self.name = [decoder decodeObjectOfClass:[NSString class] forKey:@"name"];
        self.date = [decoder decodeObjectOfClass:[NSDate class] forKey:@"date"];
        self.count = [decoder decodeIntegerForKey:@"count"];
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)encoder {
    [encoder encodeObject:self.name forKey:@"name"];
    [encoder encodeObject:self.date forKey:@"date"];
    [encoder encodeInteger:self.count forKey:@"count"];
}

+ (BOOL)supportsSecureCoding{
    return YES;
}
Run Code Online (Sandbox Code Playgroud)

对于那些想要查看帮助方法“pathForFile”的人 - 我使用“Library/Application Support”作为主要内容(因为本文表 1-3iOS 文件系统基础知识

// MyFileManager.m

- (NSString *)pathForFile:(NSString *)file subfolder:(NSString *)subfolder {

    NSString *folderPath = [self storageFolderPathWithSubfolder:subfolder];
    NSString *filePath = [folderPath stringByAppendingPathComponent:file];

    return filePath;
}

- (NSString *)storageFolderPathWithSubfolder:(NSString *)subfolder {

    NSString *storageFolderPath = [self storageFolderPath];

    if (!subfolder.length) {
        return storageFolderPath;
    }

    NSString *subfolderPath = [storageFolderPath stringByAppendingPathComponent:subfolder];

    if (![[NSFileManager defaultManager] fileExistsAtPath:subfolderPath]) { //Does directory already exist?
        [[NSFileManager defaultManager] createDirectoryAtPath:subfolderPath withIntermediateDirectories:NO attributes:@{ NSFileProtectionKey: NSFileProtectionNone } error:nil];
    }

    return subfolderPath;
}

- (NSString *)storageFolderPath {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
    NSString *storageFolderPath = [paths firstObject];

    // create folder if it doesn't exist
    NSFileManager *fileManager = [NSFileManager defaultManager];
    BOOL folderExists = [fileManager fileExistsAtPath:storageFolderPath];

    if (!folderExists) {
        [fileManager createDirectoryAtPath:storageFolderPath withIntermediateDirectories:NO attributes:@{ NSFileProtectionKey: NSFileProtectionNone } error:nil];
    }

    return storageFolderPath;
}
Run Code Online (Sandbox Code Playgroud)


Jiu*_*hao 0

这条线应该适合你:

[NSKeyedUnarchiver unarchivedObjectOfClass:NSArray.class fromData:data error:&err];
Run Code Online (Sandbox Code Playgroud)