Skip to content

Instantly share code, notes, and snippets.

@NSProgrammer
Created February 10, 2017 20:31
Show Gist options
  • Save NSProgrammer/2dab032c57ed92b9de424d236c18017b to your computer and use it in GitHub Desktop.
Save NSProgrammer/2dab032c57ed92b9de424d236c18017b to your computer and use it in GitHub Desktop.
Subclass NSCoder to encode to NSObject hierarchy (for debugging)
@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