Разархивирование закодированных данных, возвращающих ноль и неверный формат - PullRequest
0 голосов
/ 21 сентября 2018

Я добавил методы категории в 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;
}

Вызов [[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:] решает проблему.Данные хранятся и извлекаются правильно.Но сейчас этот метод устарел, поэтому мне нужно перейти к более современному методу, но я не могу определить, почему он не работает.

Ответы [ 3 ]

0 голосов
/ 05 октября 2018

У меня была похожая проблема.Я обнаружил, что должен был передать в классы для объектов, которые были в NSArray.

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

NSArray *results = [NSKeyedUnarchiver unarchivedObjectOfClasses:set fromData: rawData error: &error];
0 голосов
/ 02 января 2019

для вдохновения Я копирую свой метод для разархивирования данных из файла с помощью [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;
    }
}

А вот использование метода // ожидаемые неархивированные данные находятся в словаре

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

И небольшой совет.Ваш «MyClass» должен соответствовать протоколу «NSSecureCoding».А также реализовать 3 метода в вас "MyClass" ...

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

Для тех, кто хочет увидеть вспомогательный метод "pathForFile" - я использую в качестве основного forlder "Поддержка библиотек / приложений" (из-заэта статья Таблица 1-3 : Основы файловой системы iOS )

// 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;
}
0 голосов
/ 26 сентября 2018

эта строка должна работать для вас:

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