平常开发我们一般用NSUserDefaults来保存一些简单的数据,比如应用程序配置,NSUserDefaults提供了一系列类似于NSDictionary方法:
- (void)setObject:(nullable id)value forKey:(NSString *)defaultName
- (nullable id)objectForKey:(NSString *)defaultName;
- (void)removeObjectForKey:(NSString *)defaultName;
我们需要保存、查询、删除配置项的话,都需要传递key以调用NSUserDefaults的方法,如果能把取配置项的操作封装成属性操作,那用起来就方便多了,GVUserDefaults就是类似这样的封装。
一、GVUserDefaults用法:
详细用法见github链接GitHub - gangverk/GVUserDefaults: NSUserDefaults access via properties.支持功能如下:
1、通过定义setupDefaults方法设置配置项默认值
2、通过定义- (NSString *)transformKey:(NSString *)key方法设置键值的保存名称,比如你属性定义的是name,但是保存时想定义为DDName。
3、通过定义suitName方法让NSUserDefaults通过initWithSuiteName初始化,以支持App共享配置
二、GVUserDefaults实现原理
1、实现原理很简单,通过运行时动态加入属性的set和get方法来实现GVUserDefaults属性的设置和获取。standardUserDefaults->init->generateAccessorMethods. 方法generateAccessorMethods通过遍历GVUserDefaults扩展定义的属性,根据属性的类型,为每个属性生成set和get方法,代码如下:
- (void)generateAccessorMethods {
unsigned int count = 0;
objc_property_t *properties = class_copyPropertyList([self class], &count);
self.mapping = [NSMutableDictionary dictionary];
for (int i = 0; i < count; ++i) {
objc_property_t property = properties[i];
const char *name = property_getName(property);
const char *attributes = property_getAttributes(property);
char *getter = strstr(attributes, ",G");
if (getter) {
getter = strdup(getter + 2);
getter = strsep(&getter, ",");
} else {
getter = strdup(name);
}
SEL getterSel = sel_registerName(getter);
free(getter);
char *setter = strstr(attributes, ",S");
if (setter) {
setter = strdup(setter + 2);
setter = strsep(&setter, ",");
} else {
asprintf(&setter, "set%c%s:", toupper(name[0]), name + 1);
}
SEL setterSel = sel_registerName(setter);
free(setter);
NSString *key = [self defaultsKeyForPropertyNamed:name];
[self.mapping setValue:key forKey:NSStringFromSelector(getterSel)];
[self.mapping setValue:key forKey:NSStringFromSelector(setterSel)];
IMP getterImp = NULL;
IMP setterImp = NULL;
char type = attributes[1];
switch (type) {case Short:
case Long:
case LongLong:
case UnsignedChar:
case UnsignedShort:
case UnsignedInt:
case UnsignedLong:
case UnsignedLongLong:
getterImp = (IMP)longLongGetter;
setterImp = (IMP)longLongSetter;
break;
case Bool:
case Char:
getterImp = (IMP)boolGetter;
setterImp = (IMP)boolSetter;
break;
case Int:
getterImp = (IMP)integerGetter;
setterImp = (IMP)integerSetter;
break;
case Float:
getterImp = (IMP)floatGetter;
setterImp = (IMP)floatSetter;
break;
case Double:
getterImp = (IMP)doubleGetter;
setterImp = (IMP)doubleSetter;
break;
case Object:
getterImp = (IMP)objectGetter;
setterImp = (IMP)objectSetter;
break;
default:
free(properties);
[NSException raise:NSInternalInconsistencyException format:@"Unsupported type of property \"%s\" in class %@", name, self];
break;
}
char types[5];
snprintf(types, 4, "%c@:", type);
class_addMethod([self class], getterSel, getterImp, types);
snprintf(types, 5, "v@:%c", type);
class_addMethod([self class], setterSel, setterImp, types);
}
free(properties);
}
2、也可以通过消息转发机制实现属性set方法和get方法,通过覆盖+ (BOOL)resolveInstanceMethod:(SEL)sel来实现