Created
February 10, 2017 20:31
-
-
Save NSProgrammer/2dab032c57ed92b9de424d236c18017b to your computer and use it in GitHub Desktop.
Subclass NSCoder to encode to NSObject hierarchy (for debugging)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@interface MyDebugCoder : NSCoder | |
+ (NSDictionary *)serializedJSONWithRootObject:(id<NSCoding, NSObject>)object; | |
@end | |
@implementation MyDebugCoder | |
{ | |
NSMutableDictionary *_jsonRoot; | |
NSMutableArray *_values; | |
} | |
- (instancetype)init | |
{ | |
self = [super init]; | |
if (self) { | |
_jsonRoot = [[NSMutableDictionary alloc] init]; | |
_values = [[NSMutableArray alloc] init]; | |
_jsonRoot[@"values"] = _values; | |
} | |
return self; | |
} | |
-(void)encodeValueOfObjCType:(const char *)type at:(const void *)addr | |
{ | |
switch(*type){ | |
// bool (C++) | |
case 'B': | |
[_values addObject:@(*(const bool *)addr)]; | |
break; | |
// char | |
case 'c': | |
case 'C': | |
[_values addObject:[NSString stringWithFormat:@"%c", *(const char *)addr]]; | |
break; | |
// short | |
case 's': | |
case 'S': | |
[_values addObject:@(*(const short *)addr)]; | |
break; | |
// int | |
case 'i': | |
case 'I': | |
[_values addObject:@(*(const int *)addr)]; | |
break; | |
// long | |
case 'l': | |
case 'L': | |
[_values addObject:@(*(const long *)addr)]; | |
break; | |
// long long | |
case 'q': | |
case 'Q': | |
[_values addObject:@(*(const long long *)addr)]; | |
break; | |
// float | |
case 'f': | |
[_values addObject:@(*(const float *)addr)]; | |
break; | |
// double | |
case 'd': | |
[_values addObject:@(*(const double *)addr)]; | |
break; | |
// C-string (0 terminated) | |
case '*': | |
[_values addObject:@(*(const char**)addr)]; | |
break; | |
// Obj-C object | |
case '@': | |
[self _appendObject:*(const id *)addr]; | |
break; | |
case 'b': // bit-field "bNUMBITS" | |
case 'v': // void | |
case '[': // C-array | |
case '#': // Class | |
case ':': // selector | |
case '{': // struct | |
case '(': // union | |
case '^': // pointer to TYPE "^TYPE" | |
case '?': // unknown (including function pointers) | |
default: | |
// skip | |
NSLog(@"skip %c", *type); | |
break; | |
} | |
} | |
-(void)encodeDataObject:(NSData *)data | |
{ | |
[self _appendObject:data]; | |
} | |
-(NSInteger)versionForClassName:(NSString *)className | |
{ | |
Class objectClass = NSClassFromString(className); | |
return objectClass ? [objectClass version] : NSNotFound; | |
} | |
+ (NSDictionary *)serializedJSONWithRootObject:(id<NSCoding, NSObject>)object | |
{ | |
TNLJSONCoder *coder = [[self alloc] init]; | |
[object encodeWithCoder:coder]; | |
[coder _addObjectDescription:object]; | |
return coder->_jsonRoot; | |
} | |
+ (id)_valueForObject:(id)object | |
{ | |
if (!object) { | |
return [NSNull null]; | |
} | |
if ([object isKindOfClass:[NSString class]] || | |
[object isKindOfClass:[NSNumber class]] || | |
[object isKindOfClass:[NSDate class]] || | |
[object isKindOfClass:[NSNull class]] || | |
[object isKindOfClass:[NSURL class]]) { | |
return object; | |
} | |
if ([object isKindOfClass:[NSData class]]) { | |
return [NSString stringWithFormat:@"<%@ %@, length=%tu>", NSStringFromClass([object class]), object, [(NSData *)object length]]; | |
} | |
if ([object isKindOfClass:[NSDictionary class]]) { | |
NSMutableDictionary *valueDictionary = [NSMutableDictionary dictionaryWithCapacity:[(NSDictionary *)object count]]; | |
[(NSDictionary *)object enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { | |
key = [self _valueForObject:key]; | |
obj = [self _valueForObject:obj]; | |
valueDictionary[key] = obj; | |
}]; | |
return valueDictionary; | |
} | |
TNLJSONCoder *coder = [[[self class] alloc] init]; | |
[object encodeWithCoder:coder]; | |
[coder _addObjectDescription:object]; | |
return coder->_jsonRoot; | |
} | |
- (void)_appendObject:(const id)object | |
{ | |
[_values addObject:[[self class] _valueForObject:object]]; | |
} | |
- (void)_addObjectDescription:(id<NSObject>)object | |
{ | |
NSString *description = [object description]; | |
if (![description hasPrefix:@"<"]) { | |
description = [NSString stringWithFormat:@"<%@ %p>", NSStringFromClass([object class]), object]; | |
} | |
_jsonRoot[@"object"] = description; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment