Created
April 27, 2015 14:19
-
-
Save remirobert/fab8fe87e0edfc68a780 to your computer and use it in GitHub Desktop.
ModelContainer
This file contains hidden or 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
// | |
// Model3D.m | |
// 3DModel | |
// | |
// Created by Remi Robert on 24/04/15. | |
// Copyright (c) 2015 Remi Robert. All rights reserved. | |
// | |
#import <objc/runtime.h> | |
#import "ModelContainer.h" | |
typedef enum : NSUInteger { | |
FloatType = 'f', | |
DoubleType = 'd', | |
BoolType = 'c', | |
IntType = 'i', | |
UnsignedLongType = 'q', | |
UnsignedLongLongType = 'Q', | |
LongType = 'l', | |
ShortType = 's', | |
UnsignedIntType = 'I', | |
ObjectType = '@', | |
} NSObjectValueType; | |
#pragma mark - | |
#pragma mark NSArray categorie | |
@implementation NSArray (ModelContainer) | |
- (void)saveForKey:(NSString *)key { | |
[self removeForKey:key]; | |
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self]; | |
if (data) { | |
[[NSUserDefaults standardUserDefaults] setObject:data forKey:key]; | |
} | |
} | |
+ (instancetype)instanceForKey:(NSString *)key { | |
NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:key]; | |
if (data) { | |
NSArray *models = [NSKeyedUnarchiver unarchiveObjectWithData:data]; | |
return models; | |
} | |
return nil; | |
} | |
- (void)removeForKey:(NSString *)key { | |
[[NSUserDefaults standardUserDefaults] removeObjectForKey:key]; | |
} | |
@end | |
@implementation ModelContainer | |
#pragma mark - | |
#pragma mark Object's properties | |
- (NSMutableDictionary *)propertiesForClass:(Class)class { | |
NSMutableDictionary *results = [[NSMutableDictionary alloc] init]; | |
unsigned int outCount, i; | |
objc_property_t *properties = class_copyPropertyList(class, &outCount); | |
for(i = 0; i < outCount; i++) { | |
objc_property_t property = properties[i]; | |
NSString *pname = [NSString stringWithCString:property_getName(property) encoding:NSUTF8StringEncoding]; | |
NSString *pattrs = [NSString stringWithCString:property_getAttributes(property) encoding:NSUTF8StringEncoding]; | |
pattrs = [[pattrs componentsSeparatedByString:@","] objectAtIndex:0]; | |
pattrs = [pattrs substringFromIndex:1]; | |
[results setObject:pattrs forKey:pname]; | |
} | |
free(properties); | |
if ([class superclass] != [NSObject class]) { | |
[results addEntriesFromDictionary:[self propertiesForClass:[class superclass]]]; | |
} | |
return results; | |
} | |
- (NSDictionary *)properties { | |
return [self propertiesForClass:[self class]]; | |
} | |
#pragma mark - | |
#pragma mark Encode object's values | |
- (NSValue *)valueFromMethodSignature:(NSInvocation *)invocation withType:(char)type { | |
unsigned long long ullValue; | |
BOOL boolValue; | |
float floatValue; | |
double doubleValue; | |
NSInteger intValue; | |
unsigned long ulValue; | |
long longValue; | |
unsigned unsignedValue; | |
short shortValue; | |
[invocation invoke]; | |
switch (type) { | |
case BoolType: | |
[invocation getReturnValue:&boolValue]; | |
return [NSNumber numberWithBool:boolValue]; | |
break; | |
case FloatType: | |
[invocation getReturnValue:&floatValue]; | |
return [NSNumber numberWithFloat:floatValue]; | |
case DoubleType: | |
[invocation getReturnValue:&doubleValue]; | |
return [NSNumber numberWithDouble:doubleValue]; | |
case IntType: | |
[invocation getReturnValue:&intValue]; | |
return [NSNumber numberWithInteger:intValue]; | |
case UnsignedLongType: | |
[invocation getReturnValue:&ulValue]; | |
return [NSNumber numberWithUnsignedLong:ulValue]; | |
case UnsignedLongLongType: | |
[invocation getReturnValue:&ullValue]; | |
return [NSNumber numberWithUnsignedLongLong:ullValue]; | |
case ShortType: | |
[invocation getReturnValue:&shortValue]; | |
return [NSNumber numberWithShort:shortValue]; | |
case UnsignedIntType: | |
[invocation getReturnValue:&unsignedValue]; | |
return [NSNumber numberWithUnsignedInt:unsignedValue]; | |
case LongType: | |
[invocation getReturnValue:&longValue]; | |
return [NSNumber numberWithLong:longValue]; | |
default: | |
break; | |
} | |
return nil; | |
} | |
- (void)encodeProperties:(NSDictionary*)properties withCoder:(NSCoder *)coder { | |
for (NSString *key in properties) { | |
NSString *type = [properties objectForKey:key]; | |
id value; | |
NSMethodSignature *signature = [self methodSignatureForSelector:NSSelectorFromString(key)]; | |
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; | |
[invocation setSelector:NSSelectorFromString(key)]; | |
[invocation setTarget:self]; | |
if ([type characterAtIndex:0] == ObjectType) { | |
if ([[type componentsSeparatedByString:@"\""] count] > 1) { | |
Class class = NSClassFromString([[type componentsSeparatedByString:@"\""] objectAtIndex:1]); | |
#pragma clang diagnostic push | |
#pragma clang diagnostic ignored "-Warc-performSelector-leaks" | |
value = [self performSelector:NSSelectorFromString(key)]; | |
#pragma clang diagnostic pop | |
if([class conformsToProtocol:@protocol(NSCoding)]){ | |
[coder encodeObject:value forKey:key]; | |
} | |
} | |
} | |
else { | |
[coder encodeObject:[self valueFromMethodSignature:invocation withType:[type characterAtIndex:0]] forKey:key]; | |
} | |
} | |
} | |
#pragma mark - | |
#pragma mark Decode object's value | |
- (void)decodeProperties:(NSDictionary*)properties withCoder:(NSCoder *)coder { | |
for (NSString *key in properties) { | |
NSString *type = [properties objectForKey:key]; | |
switch ([type characterAtIndex:0]) { | |
case ObjectType: | |
if ([[type componentsSeparatedByString:@"\""] count] > 1) { | |
Class class = NSClassFromString([[type componentsSeparatedByString:@"\""] objectAtIndex:1]); | |
if ([class conformsToProtocol:@protocol(NSCoding )]){ | |
[self setValue:[coder decodeObjectForKey:key] forKey:key]; | |
} | |
} | |
break; | |
case BoolType: | |
[self setValue:@([[coder decodeObjectForKey:key] boolValue]) forKey:key]; | |
break; | |
case FloatType: | |
[self setValue:@([[coder decodeObjectForKey:key] floatValue]) forKey:key]; | |
break; | |
case DoubleType: | |
[self setValue:@([[coder decodeObjectForKey:key] doubleValue]) forKey:key]; | |
break; | |
case IntType: | |
[self setValue:@([[coder decodeObjectForKey:key] intValue]) forKey:key]; | |
break; | |
case UnsignedLongType: | |
[self setValue:@([[coder decodeObjectForKey:key] unsignedLongValue]) forKey:key]; | |
break; | |
case UnsignedLongLongType: | |
[self setValue:@([[coder decodeObjectForKey:key] unsignedLongLongValue]) forKey:key]; | |
break; | |
case LongType: | |
[self setValue:@([[coder decodeObjectForKey:key] longValue]) forKey:key]; | |
break; | |
case UnsignedIntType: | |
[self setValue:@([[coder decodeObjectForKey:key] unsignedIntValue]) forKey:key]; | |
break; | |
case ShortType: | |
[self setValue:@([[coder decodeObjectForKey:key] shortValue]) forKey:key]; | |
break; | |
default: | |
break; | |
} | |
} | |
} | |
#pragma mark - | |
#pragma mark NSCoding | |
- (void)encodeWithCoder:(NSCoder *)aCoder { | |
[self encodeProperties:[self properties] withCoder:aCoder]; | |
} | |
- (id)initWithCoder:(NSCoder *)aDecoder { | |
[self decodeProperties:[self properties] withCoder:aDecoder]; | |
return self; | |
} | |
#pragma mark - | |
#pragma mark Persistance management | |
- (void)remove { | |
if (self.key) { | |
[[NSUserDefaults standardUserDefaults] removeObjectForKey:self.key]; | |
} | |
} | |
+ (void)removeForKey:(NSString *)key { | |
[[NSUserDefaults standardUserDefaults] removeObjectForKey:key]; | |
} | |
- (void)update { | |
if (self.key) { | |
[self remove]; | |
[self save]; | |
} | |
} | |
- (void)saveForKey:(NSString *)key { | |
self.key = key; | |
[self save]; | |
} | |
- (void)save { | |
if (self.key) { | |
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:@[self]]; | |
if (data) { | |
[[NSUserDefaults standardUserDefaults] setObject:data forKey:self.key]; | |
} | |
} | |
} | |
+ (instancetype)instanceObjectForKey:(NSString *)key { | |
NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:key]; | |
if (data) { | |
NSArray *models = [NSKeyedUnarchiver unarchiveObjectWithData:data]; | |
id object = [models firstObject]; | |
if (object || ![object isKindOfClass:[ModelContainer class]]) { | |
((ModelContainer *)object).key = key; | |
return object; | |
} | |
} | |
return nil; | |
} | |
+ (NSArray *)instanceObjectsForKey:(NSString *)key { | |
NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:key]; | |
if (data) { | |
NSArray *models = [NSKeyedUnarchiver unarchiveObjectWithData:data]; | |
return models; | |
} | |
return nil; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment