Skip to content

Instantly share code, notes, and snippets.

@pixelrevision
Created February 6, 2014 01:17
Show Gist options
  • Save pixelrevision/8836729 to your computer and use it in GitHub Desktop.
Save pixelrevision/8836729 to your computer and use it in GitHub Desktop.
Property Serializer
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
typedef enum {
PXRPropSerializerTypeUnknown,
PXRPropSerializerTypeFloat,
PXRPropSerializerTypeDouble,
PXRPropSerializerTypeInt,
PXRPropSerializerTypeClass,
PXRPropSerializerTypeBool
}PXRPropSerializerType;
@interface PXRPropSerializer : NSObject <NSCoding>
- (NSArray *)ignoredProperties;
@end
#import "PXRPropSerializer.h"
@implementation PXRPropSerializer
- (id)initWithCoder:(NSCoder *)aDecoder{
self = [super init];
NSArray *classesToDecode = [self classesToSerialize];
for(Class classToDecode in classesToDecode){
NSKeyedUnarchiver *unarchiver = (NSKeyedUnarchiver *)aDecoder;
unsigned int numOfProperties;
objc_property_t *properties = class_copyPropertyList(classToDecode, &numOfProperties);
BOOL ignore;
for(int i=0; i<numOfProperties; i++) {
objc_property_t property = properties[i];
ignore = [self ignoreProperty:property];
if(!ignore){
[self decodeProperty:property withUnarchiver:unarchiver];
}
}
free(properties);
}
return self;
}
- (void)decodeProperty:(objc_property_t)property withUnarchiver:(NSKeyedUnarchiver *)unarchiver{
NSString *propertyName = [NSString stringWithCString:property_getName(property) encoding:NSASCIIStringEncoding];
PXRPropSerializerType type = [self typeForProperty:property];
switch (type) {
case PXRPropSerializerTypeUnknown:
NSLog(@"unknown property %@ not decoding", propertyName);
break;
case PXRPropSerializerTypeBool:
[self setValue:[NSNumber numberWithBool:[unarchiver decodeBoolForKey:propertyName]] forKey:propertyName];
break;
case PXRPropSerializerTypeFloat:
[self setValue:[NSNumber numberWithFloat:[unarchiver decodeFloatForKey:propertyName]] forKey:propertyName];
break;
case PXRPropSerializerTypeInt:
[self setValue:[NSNumber numberWithInt:[unarchiver decodeIntForKey:propertyName]] forKey:propertyName];
break;
case PXRPropSerializerTypeDouble:
[self setValue:[NSNumber numberWithDouble:[unarchiver decodeDoubleForKey:propertyName]] forKey:propertyName];
break;
case PXRPropSerializerTypeClass:
[self setValue:[unarchiver decodeObjectForKey:propertyName] forKey:propertyName];
break;
}
}
- (void)encodeWithCoder:(NSCoder *)aCoder{
NSArray *classesToEncode = [self classesToSerialize];
for(Class classToEncode in classesToEncode){
NSKeyedArchiver *archiver = (NSKeyedArchiver *)aCoder;
unsigned int numOfProperties;
objc_property_t *properties = class_copyPropertyList(classToEncode, &numOfProperties);
BOOL ignore;
for(int i=0; i<numOfProperties; i++) {
objc_property_t property = properties[i];
ignore = [self ignoreProperty:property];
if(!ignore){
[self encodeProperty:property withArchiver:archiver];
}
}
free(properties);
}
}
- (NSArray *)attributesOfProp:(objc_property_t)prop{
const char * propAttr = property_getAttributes(prop);
NSString *propString = [NSString stringWithUTF8String:propAttr];
NSArray *attrArray = [propString componentsSeparatedByString:@","];
return attrArray;
}
- (void)encodeProperty:(objc_property_t)property withArchiver:(NSKeyedArchiver *)archiver{
NSString *propertyName = [NSString stringWithCString:property_getName(property) encoding:NSASCIIStringEncoding];
PXRPropSerializerType type = [self typeForProperty:property];
switch (type) {
case PXRPropSerializerTypeUnknown:
NSLog(@"unknown property %@ not encoding", propertyName);
break;
case PXRPropSerializerTypeBool:
[archiver encodeBool:[[self valueForKey:propertyName] boolValue] forKey:propertyName];
break;
case PXRPropSerializerTypeFloat:
[archiver encodeFloat:[[self valueForKey:propertyName] floatValue] forKey:propertyName];
break;
case PXRPropSerializerTypeDouble:
[archiver encodeDouble:[[self valueForKey:propertyName] doubleValue] forKey:propertyName];
break;
case PXRPropSerializerTypeInt:
[archiver encodeInt:[[self valueForKey:propertyName] integerValue] forKey:propertyName];
break;
case PXRPropSerializerTypeClass:
[self encodeObjectForProperty:property withArchiver:archiver];
break;
}
}
- (void)encodeObjectForProperty:(objc_property_t)property withArchiver:(NSKeyedArchiver *)archiver{
NSString *propertyName = [NSString stringWithCString:property_getName(property) encoding:NSASCIIStringEncoding];
id object = [self valueForKey:propertyName];
if(![[object class] conformsToProtocol:@protocol(NSCoding)]){
NSLog(@"Not saving %@. does not conform to NSCoding", propertyName);
}
[archiver encodeObject:object forKey:propertyName];
}
- (PXRPropSerializerType)typeForProperty:(objc_property_t)property{
const char * type = property_getAttributes(property);
NSString *typeString = [NSString stringWithUTF8String:type];
NSArray *attributes = [typeString componentsSeparatedByString:@","];
NSString *typeAttribute = [attributes objectAtIndex:0];
NSString *propertyType = [typeAttribute substringFromIndex:1];
const char *rawPropertyType = [propertyType UTF8String];
if(strcmp(rawPropertyType, @encode(float)) == 0){
return PXRPropSerializerTypeFloat;
}else if (strcmp(rawPropertyType, @encode(int)) == 0){
return PXRPropSerializerTypeInt;
}else if (strcmp(rawPropertyType, @encode(double)) == 0){
return PXRPropSerializerTypeDouble;
}else if (strcmp(rawPropertyType, @encode(BOOL)) == 0){
return PXRPropSerializerTypeBool;
}else if (strcmp(rawPropertyType, @encode(id)) == 0){
return PXRPropSerializerTypeClass;
}
if([typeAttribute hasPrefix:@"T@"] && [typeAttribute length] > 1){
NSString *typeClassName = [typeAttribute substringWithRange:NSMakeRange(3, [typeAttribute length]-4)];
Class typeClass = NSClassFromString(typeClassName);
if(typeClass != nil){
return PXRPropSerializerTypeClass;
}
}
return PXRPropSerializerTypeUnknown;
}
- (NSArray *)classesToSerialize{
NSMutableArray *classes = [[NSMutableArray alloc] init];
BOOL finished = NO;
Class superClass = [self class];
while(!finished){
if(superClass == [PXRPropSerializer class] || superClass == [NSObject class]){
finished = YES;
}else{
[classes addObject:superClass];
superClass = [superClass superclass];
}
}
return classes;
}
- (NSArray *)ignoredProperties{
return @[];
}
- (BOOL)ignoreProperty:(objc_property_t)property{
NSString *propertyName = [NSString stringWithCString:property_getName(property) encoding:NSASCIIStringEncoding];
NSArray *ignored = [self ignoredProperties];
for(NSString *ignore in ignored){
if([propertyName isEqualToString:ignore]){
return YES;
}
}
return NO;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment