简体   繁体   English

解压缩编码数据返回 nil 和不正确的格式

[英]Unarchiving encoded data returning nil and incorrect format

I have added category methods to NSUserDefaults to store and retrieve encoded objects (in this case, an NSArray).我已经向 NSUserDefaults 添加了类别方法来存储和检索编码对象(在本例中为 NSArray)。 I am having a problem retrieving the encoded data.我在检索编码数据时遇到问题。 Here is my code:这是我的代码:

- (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;
}

Calling [[NSUserDefaults standardDefaults] encodeObject:object forKey:key] should encode the object passed and store it in defaults, and then calling [[NSUserDefaults standardDefaults] decodeObjectForKey:key class:aClass should return the encoded object.调用[[NSUserDefaults standardDefaults] encodeObject:object forKey:key]应该对传递的对象进行编码并将其存储在默认值中,然后调用[[NSUserDefaults standardDefaults] decodeObjectForKey:key class:aClass应该返回编码的对象。

The problem is that [NSKeyedUnarchiver unarchivedObjectOfClass:fromData:error:] is returning nil , and the error text is logged as The data couldn't be read because it isn't in the correct format.问题是[NSKeyedUnarchiver unarchivedObjectOfClass:fromData:error:]返回nil ,错误文本被记录为The data couldn't be read because it isn't in the correct format. . . The data retrieved using [self objectForKey:] is of type __NSCFData .使用检索到的数据[self objectForKey:]是类型的__NSCFData I don't know if this is relevant since AFAIK __NSCFData is toll-free bridged to NSData.我不知道这是否相关,因为 AFAIK __NSCFData 是免费桥接到 NSData 的。

Replacing [NSKeyedUnarchiver unarchivedObjectOfClass:fromData:error:] with [NSKeyedUnarchiver unarchiveObjectWithData:] solves the problem.[NSKeyedUnarchiver unarchivedObjectOfClass:fromData:error:]替换[NSKeyedUnarchiver unarchivedObjectOfClass:fromData:error:] [NSKeyedUnarchiver unarchiveObjectWithData:]解决了这个问题。 The data are stored and retrieved correctly.数据被正确存储和检索。 But this method is now deprecated so I need to move to the more modern method, but cannot identify why it isn't working.但是这种方法现在已被弃用,所以我需要转向更现代的方法,但无法确定为什么它不起作用。

I had a similar issue.我有一个类似的问题。 I found that I had to pass in the classes for the objects that were in the NSArray.我发现我必须为 NSArray 中的对象传递类。

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

NSArray *results = [NSKeyedUnarchiver unarchivedObjectOfClasses:set fromData: rawData error: &error];

None of the other answers cover this well enough for custom classes (or make it clear enough) so hopefully I can help some people by adding an answer!对于自定义类(或使其足够清晰),其他答案都没有足够好地涵盖这一点,因此希望我可以通过添加答案来帮助某些人!

If your data represents an archived array of a custom class that has various class properties then it's not enough to just provide the array class and your custom class, you need to provide all the classes that your custom class uses to, eg:如果您的数据表示具有各种类属性的自定义类的存档数组,那么仅提供数组类和自定义类是不够的,您需要提供自定义类使用的所有类,例如:

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];

for inspiration I'm coping my method for unarchiving data from file by [NSKeyedUnarchiver unarchivedObjectOfClasses: fromData:]... There is small improvement: you don't need to list all classes in the method call because the most used classes are added automatically in the method body.为了获得灵感,我正在通过 [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;
    }
}

And here is usage of the method // expected unarchived data are in dictionary这是该方法的用法 // 预期的未归档数据在字典中

NSDictionary *dictionary = [FILE_MANAGER getDataFromFile:@"testfile" inSubfolder:nil dataClasses:@[[MyClass class]]];

And a small advice.还有一个小建议。 Your "MyClass" must conform the "NSSecureCoding" protocol.您的“MyClass”必须符合“NSSecureCoding”协议。 And also implement 3 method in you "MyClass"...并在你的“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;
}

For those who want to see the helping method "pathForFile" - I use as the main forlder "Library/Application Support" (because of this article Table 1-3 : iOS File System Basics )对于那些想要查看帮助方法“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;
}

这条线应该适合你:

[NSKeyedUnarchiver unarchivedObjectOfClass:NSArray.class fromData:data error:&err];

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM