これでいいのか分からないけど設定モデル考えた

アプリの設定情報を保存する場合、NSUserDefaultやCoreDataやrealmや色々選択肢はあると思います。

僕はタイムラインなどの大きくなりがちなデータはdb、設定ファイル等のミニマムなものはNSUserDefaultを使うようにして使い分けています。

NSUserDefaultはまずstandardUserDefaultsでインスタンスを生成してそこにキーを指定してバンバン入れていきます。

つまり、1つ設定する場合

- インスタンス生成
- キーを作る
- キーを使って保存
- 書き込み
- キーを使って呼び出す

といろいろしないといけないわけです。
1つならまだしも、いくつも設定を書くと膨大に同じようなコードが生まれるわけです。
また、キーが文字列なので間違えると嫌です。
\#defineしたとしても、本当にこれって必要なんでしょうか。

そこで、モデルを作りプロパティを設定しプロパティに値が入ったらすぐに保存されるっていうのはどうでしょうか。

```objc:Configure.h
#import <Foundation/Foundation.h>

@interface Configure : NSObject
+ (instancetype)sharedInstance;

/**support type
 * NSInteger
 * BOOL
 * URL
 * other Object (NSString ...)
 */

@end


```

```objc:Configure.m
#import "Configure.h"
#import <objc/runtime.h>

@interface Configure (){
    NSUserDefaults*_df;
    NSMutableDictionary*_typeDic;
}
@end

@implementation Configure

+ (instancetype)sharedInstance{
    static Configure* sharedInstance;
    static dispatch_once_t once;
    dispatch_once( &once, ^{
        sharedInstance = [[self alloc] init];
    });
    return sharedInstance;
}

- (instancetype)init
{
    self = [super init];
    if (self) {
        _df = [NSUserDefaults standardUserDefaults];
        _typeDic = [NSMutableDictionary dictionary];
        [self setObserverAllProperties];
        [self restoreValue];
    }
    return self;
}

- (void)restoreValue{
    for (NSString*name in [self propertyNames]) {
        id object = [_df objectForKey:name];
        if (object) {
            [self setValue:object forKey:name];
        }
    }
}

- (void)dealloc{
    [self removeObserverAllProperties];
}

- (void)setObserverAllProperties{
    for (NSString*name in [self propertyNames]) {
        [self addObserver:self forKeyPath:name options:NSKeyValueObservingOptionNew context:nil];
    }
}

- (void)removeObserverAllProperties{
    for (NSString*name in [self propertyNames]) {
        [self removeObserver:self forKeyPath:name];
    }
}

- (NSArray*)propertyNames{
    NSMutableArray*names = [NSMutableArray new];
    unsigned int outCount, i;
    objc_property_t *properties = class_copyPropertyList([self class], &outCount);
    for (i = 0; i < outCount; i++) {
        objc_property_t property = properties[i];
        const char *name = property_getName(property);
        NSString *propertyName = [NSString stringWithUTF8String:name];
        NSString *propertyType = [NSString stringWithUTF8String:getPropertyType(property)];
        [names addObject:propertyName];
        //NSLog(@"%@:%@",propertyName,propertyType);
        [_typeDic setObject:propertyType forKey:propertyName];
    }
    free(properties);
    return names;
}

static const char * getPropertyType(objc_property_t property) {
    const char *attributes = property_getAttributes(property);
    char buffer[1 + strlen(attributes)];
    strcpy(buffer, attributes);
    char *state = buffer, *attribute;
    while ((attribute = strsep(&state, ",")) != NULL) {
        if (attribute[0] == 'T' && attribute[1] != '@') {
            return (const char *)[[NSData dataWithBytes:(attribute + 1) length:strlen(attribute) - 1] bytes];
        }else if (attribute[0] == 'T' && attribute[1] == '@' && strlen(attribute) == 2) {
            return "id";
        }else if (attribute[0] == 'T' && attribute[1] == '@') {
            return (const char *)[[NSData dataWithBytes:(attribute + 3) length:strlen(attribute) - 4] bytes];
        }
    }
    return "";
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
    if ([_typeDic[keyPath] isEqualToString:@"B"]) {//Bool value
        [_df setBool:[self valueForKey:keyPath] forKey:keyPath];
    }else if ([_typeDic[keyPath] isEqualToString:@"q"]) {//NSinteger value
        [_df setInteger:(NSInteger)[self valueForKey:keyPath] forKey:keyPath];
    }else{//NSString value
        [_df setObject:[self valueForKey:keyPath] forKey:keyPath];
    }
    [_df synchronize];
}
@end
```

何をしてるかというと、モデルの全てのプロパティにKVOで監視をかけて値が入ったらそのプロパティ名と同じ名前のキーでNSUserDefaultに保存しています。
シングルトンなのはアプリの設定に唯一性があるからです。

#メリット
プロパティを設定するだけで設定モデルが作れる！

#デメリット
保存出来るオブジェクトが制限される（どうにか出来そう）
なんか気持ち悪い（特にobserveValueForKeyPathのところ）
初期値設定（どうにか出来そう）


あまり検証してないので、これでいいのか分かりませんがいかがでしょうか。

ツッコミお待ちしております。