├── NSObject+NSCoding.h ├── Archiver.h ├── README ├── Archiver.m └── NSObject+NSCoding.m /NSObject+NSCoding.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+NSCoding.h 3 | // OpenStack 4 | // 5 | // Created by Michael Mayo on 3/4/11. 6 | // The OpenStack project is provided under the Apache 2.0 license. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface NSObject (NSCoding) 13 | 14 | - (void)autoEncodeWithCoder: (NSCoder *)coder; 15 | - (void)autoDecode:(NSCoder *)coder; 16 | - (NSDictionary *)properties; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /Archiver.h: -------------------------------------------------------------------------------- 1 | // 2 | // Archiver.h 3 | // OpenStack 4 | // 5 | // Created by Mike Mayo on 10/4/10. 6 | // The OpenStack project is provided under the Apache 2.0 license. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface Archiver : NSObject { 13 | 14 | } 15 | 16 | + (id)retrieve:(NSString *)key; 17 | + (BOOL)persist:(id)object key:(NSString *)key; 18 | + (BOOL)delete:(NSString *)key; 19 | + (BOOL)deleteEverything; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | NSObject+NSCoding and Archiver 2 | ------------------------------ 3 | 4 | Mike Mayo 5 | Rackspace Mobile Apps 6 | mike@overhrd.com 7 | twitter: @greenisus 8 | 9 | These are some simple classes to make object persistence with NSCoding easier. This code was extracted from 10 | the Rackspace Cloud / OpenStack iOS app at http://launchpad.net/openstack-ios 11 | 12 | INSTALLATION 13 | 14 | To install, simply drag Archiver.h, Archiver.m, NSObject+NSCoding.h, and NSObject+NSCoding.m into your project. 15 | 16 | Then, right click Frameworks in Groups & Files and choose Add -> Existing Frameworks... and choose libobjc.A.dylib. 17 | 18 | USAGE 19 | 20 | -- Archiver 21 | 22 | This class will read and write objects that conform to the NSCoding protocol to disk. 23 | 24 | Archiver Usage: 25 | 26 | SomeClass *myObject = [[[SomeClass alloc] init] autorelease]; 27 | myObject.someProperty = @"Hello world"; 28 | 29 | [Archiver persist:myObject key:@"myObject"]; 30 | 31 | // later on somewhere else... 32 | 33 | SomeClass *myObject = [Archiver retrieve:@"myObject"]; 34 | 35 | -- NSObject+NSCoding 36 | 37 | This category simplifies implementing NSCoding by iterating over the properties of your 38 | class and encoding/decoding them for you. It persists primitives (such as ints and floats) 39 | as well as any objects that conform to NSCoding. 40 | 41 | NSObject+NSCoding Usage: 42 | 43 | In your class header, conform to NSCoding: 44 | 45 | @interface Model : NSObject { 46 | //... 47 | } 48 | 49 | In your class implementation, call the automatic methods: 50 | 51 | - (void)encodeWithCoder:(NSCoder *)coder { 52 | [self autoEncodeWithCoder:coder]; 53 | } 54 | 55 | - (id)initWithCoder:(NSCoder *)coder { 56 | if (self = [super init]) { 57 | [self autoDecode:coder]; 58 | } 59 | return self; 60 | } 61 | -------------------------------------------------------------------------------- /Archiver.m: -------------------------------------------------------------------------------- 1 | // 2 | // Archiver.m 3 | // OpenStack 4 | // 5 | // Created by Mike Mayo on 10/4/10. 6 | // The OpenStack project is provided under the Apache 2.0 license. 7 | // 8 | 9 | #import "Archiver.h" 10 | 11 | @implementation Archiver 12 | 13 | + (id)retrieve:(NSString *)key { 14 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 15 | NSString *documentsDirectory = [paths objectAtIndex:0]; 16 | NSString *filePath = [documentsDirectory stringByAppendingString:[NSString stringWithFormat:@"/%@.archive", key]]; 17 | return [[[NSKeyedUnarchiver unarchiveObjectWithFile:filePath] retain] autorelease]; 18 | } 19 | 20 | + (BOOL)persist:(id)object key:(NSString *)key { 21 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 22 | NSString *documentsDirectory = [paths objectAtIndex:0]; 23 | NSString *filePath = [documentsDirectory stringByAppendingString:[NSString stringWithFormat:@"/%@.archive", key]]; 24 | return [NSKeyedArchiver archiveRootObject:object toFile:filePath]; 25 | } 26 | 27 | + (BOOL)delete:(NSString *)key { 28 | NSFileManager *fileManager = [NSFileManager defaultManager]; 29 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 30 | NSString *documentsDirectory = [paths objectAtIndex:0]; 31 | NSString *filePath = [documentsDirectory stringByAppendingString:[NSString stringWithFormat:@"/%@.archive", key]]; 32 | return [fileManager removeItemAtPath:filePath error:NULL]; 33 | } 34 | 35 | + (BOOL)deleteEverything { 36 | BOOL result = YES; 37 | NSFileManager *fileManager = [NSFileManager defaultManager]; 38 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 39 | NSString *documentsDirectory = [paths objectAtIndex:0]; 40 | 41 | NSArray *files = [fileManager contentsOfDirectoryAtPath:documentsDirectory error:NULL]; 42 | 43 | for (int i = 0; i < [files count]; i++) { 44 | NSString *path = [files objectAtIndex:i]; 45 | result = result && [fileManager removeItemAtPath:path error:NULL]; 46 | } 47 | 48 | return result; 49 | } 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /NSObject+NSCoding.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+NSCoding.m 3 | // OpenStack 4 | // 5 | // Created by Michael Mayo on 3/4/11. 6 | // The OpenStack project is provided under the Apache 2.0 license. 7 | // 8 | 9 | #import "NSObject+NSCoding.h" 10 | #import 11 | 12 | 13 | @implementation NSObject (NSCoding) 14 | 15 | - (NSMutableDictionary *)propertiesForClass:(Class)klass { 16 | 17 | NSMutableDictionary *results = [[[NSMutableDictionary alloc] init] autorelease]; 18 | 19 | unsigned int outCount, i; 20 | objc_property_t *properties = class_copyPropertyList(klass, &outCount); 21 | for(i = 0; i < outCount; i++) { 22 | objc_property_t property = properties[i]; 23 | 24 | NSString *pname = [NSString stringWithCString:property_getName(property) encoding:NSUTF8StringEncoding]; 25 | NSString *pattrs = [NSString stringWithCString:property_getAttributes(property) encoding:NSUTF8StringEncoding]; 26 | 27 | pattrs = [[pattrs componentsSeparatedByString:@","] objectAtIndex:0]; 28 | pattrs = [pattrs substringFromIndex:1]; 29 | 30 | [results setObject:pattrs forKey:pname]; 31 | } 32 | free(properties); 33 | 34 | if ([klass superclass] != [NSObject class]) { 35 | [results addEntriesFromDictionary:[self propertiesForClass:[klass superclass]]]; 36 | } 37 | 38 | return results; 39 | } 40 | 41 | - (NSDictionary *)properties { 42 | return [self propertiesForClass:[self class]]; 43 | } 44 | 45 | - (void)autoEncodeWithCoder:(NSCoder *)coder { 46 | NSDictionary *properties = [self properties]; 47 | for (NSString *key in properties) { 48 | NSString *type = [properties objectForKey:key]; 49 | id value; 50 | unsigned long long ullValue; 51 | long long llValue; 52 | BOOL boolValue; 53 | float floatValue; 54 | double doubleValue; 55 | NSInteger intValue; 56 | unsigned long ulValue; 57 | long longValue; 58 | unsigned unsignedValue; 59 | short shortValue; 60 | NSString *className; 61 | NSMethodSignature *signature = [self methodSignatureForSelector:NSSelectorFromString(key)]; 62 | NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; 63 | [invocation setSelector:NSSelectorFromString(key)]; 64 | [invocation setTarget:self]; 65 | 66 | switch ([type characterAtIndex:0]) { 67 | case '@': // object 68 | if ([[type componentsSeparatedByString:@"\""] count] > 1) { 69 | className = [[type componentsSeparatedByString:@"\""] objectAtIndex:1]; 70 | Class class = NSClassFromString(className); 71 | value = [self performSelector:NSSelectorFromString(key)]; 72 | 73 | // only decode if the property conforms to NSCoding 74 | if([class conformsToProtocol:@protocol(NSCoding)]){ 75 | [coder encodeObject:value forKey:key]; 76 | } 77 | } 78 | break; 79 | case 'B': 80 | [invocation invoke]; 81 | [invocation getReturnValue:&boolValue]; 82 | [coder encodeObject:[NSNumber numberWithBool:boolValue] forKey:key]; 83 | break; 84 | case 'c': // bool 85 | [invocation invoke]; 86 | [invocation getReturnValue:&boolValue]; 87 | [coder encodeObject:[NSNumber numberWithBool:boolValue] forKey:key]; 88 | break; 89 | case 'f': // float 90 | [invocation invoke]; 91 | [invocation getReturnValue:&floatValue]; 92 | [coder encodeObject:[NSNumber numberWithFloat:floatValue] forKey:key]; 93 | break; 94 | case 'd': // double 95 | [invocation invoke]; 96 | [invocation getReturnValue:&doubleValue]; 97 | [coder encodeObject:[NSNumber numberWithDouble:doubleValue] forKey:key]; 98 | break; 99 | case 'i': // int 100 | [invocation invoke]; 101 | [invocation getReturnValue:&intValue]; 102 | [coder encodeObject:[NSNumber numberWithInteger:intValue] forKey:key]; 103 | break; 104 | case 'L': // unsigned long 105 | [invocation invoke]; 106 | [invocation getReturnValue:&ulValue]; 107 | [coder encodeObject:[NSNumber numberWithUnsignedLong:ulValue] forKey:key]; 108 | break; 109 | case 'Q': // unsigned long long 110 | [invocation invoke]; 111 | [invocation getReturnValue:&ullValue]; 112 | [coder encodeObject:[NSNumber numberWithUnsignedLongLong:ullValue] forKey:key]; 113 | break; 114 | case 'q': // long long 115 | [invocation invoke]; 116 | [invocation getReturnValue:&llValue]; 117 | [coder encodeObject:[NSNumber numberWithLongLong:llValue] forKey:key]; 118 | break; 119 | case 'l': // long 120 | [invocation invoke]; 121 | [invocation getReturnValue:&longValue]; 122 | [coder encodeObject:[NSNumber numberWithLong:longValue] forKey:key]; 123 | break; 124 | case 's': // short 125 | [invocation invoke]; 126 | [invocation getReturnValue:&shortValue]; 127 | [coder encodeObject:[NSNumber numberWithShort:shortValue] forKey:key]; 128 | break; 129 | case 'I': // unsigned 130 | [invocation invoke]; 131 | [invocation getReturnValue:&unsignedValue]; 132 | [coder encodeObject:[NSNumber numberWithUnsignedInt:unsignedValue] forKey:key]; 133 | break; 134 | default: 135 | break; 136 | } 137 | } 138 | } 139 | 140 | - (void)autoDecode:(NSCoder *)coder { 141 | NSDictionary *properties = [self properties]; 142 | for (NSString *key in properties) { 143 | NSString *ivarKey = [@"_"stringByAppendingString:key]; 144 | NSString *type = [properties objectForKey:key]; 145 | NSNumber *number; 146 | unsigned int addr; 147 | NSInteger i; 148 | CGFloat f; 149 | BOOL b; 150 | double d; 151 | unsigned long ul; 152 | unsigned long long ull; 153 | long longValue; 154 | long long longLongValue; 155 | unsigned unsignedValue; 156 | short shortValue; 157 | Ivar ivar; 158 | 159 | bool *varIndexBool; 160 | float *varIndexFloat; 161 | double *varIndexDouble; 162 | unsigned long long *varIndexULongLong; 163 | unsigned long *varIndexULong; 164 | long *varIndexLong; 165 | long long *varIndexLongLong; 166 | unsigned *varU; 167 | short *varShort; 168 | 169 | NSString *className; 170 | switch ([type characterAtIndex:0]) { 171 | case '@': // object 172 | if ([[type componentsSeparatedByString:@"\""] count] > 1) { 173 | className = [[type componentsSeparatedByString:@"\""] objectAtIndex:1]; 174 | Class class = NSClassFromString(className); 175 | // only decode if the property conforms to NSCoding 176 | if ([class conformsToProtocol:@protocol(NSCoding )]){ 177 | id value = [coder decodeObjectForKey:key]; 178 | // object_setInstanceVariable(self, [ivarKey UTF8String], &value); 179 | [self setValue:value forKey:key]; 180 | } 181 | } 182 | break; 183 | case 'B': // bool 184 | number = [coder decodeObjectForKey:key]; 185 | b = [number boolValue]; 186 | 187 | if ((ivar = class_getInstanceVariable([self class], [ivarKey UTF8String]))) { 188 | varIndexBool = (bool *)(void **)((char *)self + ivar_getOffset(ivar)); 189 | *varIndexBool = b; 190 | } 191 | break; 192 | case 'c': // bool 193 | number = [coder decodeObjectForKey:key]; 194 | b = [number boolValue]; 195 | if ((ivar = class_getInstanceVariable([self class], [ivarKey UTF8String]))) { 196 | varIndexBool = (bool *)(void **)((char *)self + ivar_getOffset(ivar)); 197 | *varIndexBool = b; 198 | } 199 | break; 200 | case 'f': // float 201 | number = [coder decodeObjectForKey:key]; 202 | f = [number floatValue]; 203 | if ((ivar = class_getInstanceVariable([self class], [ivarKey UTF8String]))) { 204 | varIndexFloat = (float *)(void **)((char *)self + ivar_getOffset(ivar)); 205 | *varIndexFloat = f; 206 | } 207 | break; 208 | case 'd': // double 209 | number = [coder decodeObjectForKey:key]; 210 | d = [number doubleValue]; 211 | if ((ivar = class_getInstanceVariable([self class], [ivarKey UTF8String]))) { 212 | varIndexDouble = (double *)(void **)((char *)self + ivar_getOffset(ivar)); 213 | *varIndexDouble = d; 214 | } 215 | break; 216 | case 'i': // int 217 | number = [coder decodeObjectForKey:key]; 218 | i = [number intValue]; 219 | if ((ivar = class_getInstanceVariable([self class], [ivarKey UTF8String]))) { 220 | varIndexLong = (long *)(void **)((char *)self + ivar_getOffset(ivar)); 221 | *varIndexLong = i; 222 | } 223 | break; 224 | case 'L': // unsigned long 225 | number = [coder decodeObjectForKey:key]; 226 | ul = [number unsignedLongValue]; 227 | 228 | if ((ivar = class_getInstanceVariable([self class], [ivarKey UTF8String]))) { 229 | varIndexULong = (unsigned long *)(void **)((char *)self + ivar_getOffset(ivar)); 230 | *varIndexULong = ul; 231 | } 232 | 233 | break; 234 | case 'Q': // unsigned long long 235 | number = [coder decodeObjectForKey:key]; 236 | ull = [number unsignedLongLongValue]; 237 | addr = (unsigned int)&ull; 238 | 239 | if ((ivar = class_getInstanceVariable([self class], [ivarKey UTF8String]))) { 240 | varIndexULongLong = (unsigned long long *)(void **)((char *)self + ivar_getOffset(ivar)); 241 | *varIndexULongLong = ull; 242 | } 243 | break; 244 | case 'l': // long 245 | number = [coder decodeObjectForKey:key]; 246 | longValue = [number longValue]; 247 | if ((ivar = class_getInstanceVariable([self class], [ivarKey UTF8String]))) { 248 | varIndexLong = (long *)(void **)((char *)self + ivar_getOffset(ivar)); 249 | *varIndexLong = longValue; 250 | } 251 | break; 252 | case 'I': // unsigned 253 | number = [coder decodeObjectForKey:key]; 254 | unsignedValue = [number unsignedIntValue]; 255 | if ((ivar = class_getInstanceVariable([self class], [ivarKey UTF8String]))) { 256 | varU = (unsigned *)(void **)((char *)self + ivar_getOffset(ivar)); 257 | *varU = unsignedValue; 258 | } 259 | break; 260 | case 's': // short 261 | number = [coder decodeObjectForKey:key]; 262 | shortValue = [number shortValue]; 263 | if ((ivar = class_getInstanceVariable([self class], [ivarKey UTF8String]))) { 264 | varShort = (short *)(void **)((char *)self + ivar_getOffset(ivar)); 265 | *varShort = shortValue; 266 | } 267 | break; 268 | case 'q': // long long 269 | number = [coder decodeObjectForKey:key]; 270 | longLongValue = [number longLongValue]; 271 | if ((ivar = class_getInstanceVariable([self class], [ivarKey UTF8String]))) { 272 | varIndexLongLong = (long long *)(void **)((char *)self + ivar_getOffset(ivar)); 273 | *varIndexLongLong = longLongValue; 274 | } 275 | break; 276 | 277 | default: 278 | break; 279 | } 280 | } 281 | } 282 | 283 | @end 284 | --------------------------------------------------------------------------------