From f256617636d354a52ddc547895b370c4d4d674cd Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Wed, 28 Sep 2016 12:07:22 -0400 Subject: [PATCH] If we can't find a class definition, don't explode, just log. If we have a serialized object in our data store that wasn't properly removed, we can be faced with an exception when enumerating objects in the database, e.g. when we add a new Yap Index. // FREEBIE --- src/Storage/TSStorageManager.m | 38 +++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/src/Storage/TSStorageManager.m b/src/Storage/TSStorageManager.m index f9b39857a..f05611261 100644 --- a/src/Storage/TSStorageManager.m +++ b/src/Storage/TSStorageManager.m @@ -32,19 +32,39 @@ static NSString *keychainDBPassAccount = @"TSDatabasePass"; @end -@implementation TSRecipient +@interface OWSUnknownObject : NSObject + +@end + +/** + * A default object to returned when we can't deserialize an object from YapDB. This can prevent crashes when + * old objects linger after their definition file is removed. The danger is that, the objects can lay in wait + * until the next time a DB extension is added and we necessarily enumerate the entire DB. + */ +@implementation OWSUnknownObject - (instancetype)initWithCoder:(NSCoder *)aDecoder { - DDLogWarn(@"Ignoring decoding signal recipient with coder."); - - self = [super init]; return nil; } - (void)encodeWithCoder:(NSCoder *)aCoder { - DDLogWarn(@"Ignoring encoding signal recipient with coder."); + +} + +@end + +@interface OWSUnarchiverDelegate : NSObject + +@end + +@implementation OWSUnarchiverDelegate + +- (nullable Class)unarchiver:(NSKeyedUnarchiver *)unarchiver cannotDecodeObjectOfClassName:(NSString *)name originalClasses:(NSArray *)classNames +{ + DDLogError(@"[OWSUnarchiverDelegate] Ignoring unknown class name: %@. Was the class definition deleted?", name); + return [OWSUnknownObject class]; } @end @@ -86,13 +106,17 @@ static NSString *keychainDBPassAccount = @"TSDatabasePass"; **/ + (YapDatabaseDeserializer)logOnFailureDeserializer { + OWSUnarchiverDelegate *unarchiverDelegate = [OWSUnarchiverDelegate new]; + return ^id(NSString __unused *collection, NSString __unused *key, NSData *data) { if (!data || data.length <= 0) { return nil; } @try { - return [NSKeyedUnarchiver unarchiveObjectWithData:data]; + NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data]; + unarchiver.delegate = unarchiverDelegate; + return [unarchiver decodeObjectForKey:@"root"]; } @catch (NSException *exception) { // Sync log in case we bail. DDLogError(@"%@ Unarchiving key:%@ from collection:%@ and data %@ failed with error: %@", @@ -103,8 +127,6 @@ static NSString *keychainDBPassAccount = @"TSDatabasePass"; exception.reason); DDLogError(@"%@ Raising exception.", self.tag); @throw exception; - // DDLogWarn(@"%@ Ignoring exception.", self.tag); - // return nil; } }; }