Как разархивировать данные с помощью unarchivedObjectOfClass: fromData: error :? - PullRequest
2 голосов
/ 29 марта 2019

Я использую unarchiveObjectWithData для разархивирования данных из NSUserDefaults, и это работает хорошо, но в iOS 12.0 это устарело.Xcode предлагает использовать unarchivedObjectOfClass:fromData:error:, но этот метод не работает.

У меня есть массив объектов моего класса для разархивации.

Я пытался использовать unarchivedObjectOfClass:fromData:error:, также unarchivedObjectOfClasses:fromData:error: со всеми классами (NSArray, NSString, MYCLASS ..)

Работает

NSArray *stored = [NSKeyedUnarchiver unarchiveObjectWithData:data];

Не:

NSArray *stored = [NSKeyedUnarchiver unarchivedObjectOfClass:[MYCLASS class] fromData:data error:&error];

Я получаю ”Данные не могли 'не читается, потому что он не в правильном формате. „

1 Ответ

1 голос
/ 03 апреля 2019

unarchivedObjectOfClass:fromData:error кажется довольно недокументированным, но я понял это. В вашем случае, когда вы разархивируете NSArray и предполагаете, что содержимое массива являются стандартными классами, такими как NSString, тогда это должно работать для вас:

NSArray *stored = [NSKeyedUnarchiver unarchivedObjectOfClass:[NSArray class] fromData:data error:&error];

Однако я разархивировал пользовательский объект PurchasedSubscription, и это также применимо, если ваш NSArray содержит какие-либо пользовательские классы ... Во-первых, objectOfClass от метода к архиву должен быть классом того, что вы ожидаете получить.

PurchasedSubscription *purchasedSubscription = [NSKeyedUnarchiver unarchivedObjectOfClass:PurchasedSubscription.class fromData:data error:&error];

Затем ваш пользовательский класс должен соответствовать NSSecureCoding, поэтому добавьте его в интерфейс класса. Я предполагаю, что вы уже внедрили NSCoding.

@interface PurchasedSubscription : NSObject <NSCoding, NSSecureCoding>

@end

Затем ваш класс должен переопределить supportsSecureCoding, чтобы подтвердить, что он поддерживается

+ (BOOL)supportsSecureCoding
{
    return YES;
}

Далее, в вашем методе initWithCoder: вам нужно использовать decodeObjectOfClass:key: вместо decodeObjectForKey при декодировании каждого свойства, снова устанавливая параметр Class в качестве типа класса того, что декодируется.

- (nullable instancetype)initWithCoder:(nonnull NSCoder *)aDecoder
{
    self = [self init];

    if (self)
    {
        ReceiptInfo *receiptInfo = [aDecoder decodeObjectOfClass:[ReceiptInfo class] forKey:@"receiptInfo"];
        return [self initWithReceiptInfo:receiptInfo];
    }

    return self;
}

Как вы можете видеть здесь, этот класс также декодирует другой пользовательский класс ReceiptInfo, поэтому мне пришлось повторить этот процесс с этим классом, чтобы все это заработало.

Теперь, когда я использую

PurchasedSubscription *purchasedSubscription = [NSKeyedUnarchiver unarchivedObjectOfClass:PurchasedSubscription.class fromData:data error:&error];

он надежно декодирует класс PurchasedSubscription, надежно декодируя класс ReceiptInfo, поскольку на каждом шаге он знает, каким должен быть тип класса, прежде чем декодировать его.


Записка на обороте NSEncoding. Вам необходимо использовать метод

archivedDataWithRootObject:requiringSecureCoding:error:

вместо

archivedDataWithRootObject:

С этим вы не передаете класс объекта, вы передаете реальный объект. В моем случае я создаю объект примерно так

PurchasedSubscription *validSub = [[PurchasedSubscription alloc] initWithReceiptInfo:latestReceipt];

и затем закодируйте его следующим образом

NSData *data = [NSKeyedArchiver archivedDataWithRootObject:validSub requiringSecureCoding:YES error:&error];
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...