在我的项目中,我有一个设置类,其中的属性具有访问NSUserDefaults
的自定义设置器,以使一切变得更简单。这个想法是Settings
类有
@property NSString *name
具有从NSUserDefaults
获取名称值的自定义getter以及在那里保存新值的setter。通过这种方式,在整个项目中,我只与Settings类进行交互,以管理用户定义的首选项。问题是,编写所有getter和setter似乎过于重复(我有大约50个属性),并且想创建一个适用于所有变量的setter和一个getter。我唯一的问题是如何在setter中获取变量的名称。
最后一个问题是:是否有可能在getter或setter中查找被调用函数的属性?
如果你有其他方法,我也会很感激,但考虑到我想将所有NSUserDefaults
内容保留在一个课程中,我无法想到一个不包括写50个getter的替代方法和塞特斯。
谢谢!
答案 0 :(得分:3)
在这种情况下,setter和getter很简单,你可以这样做:
- (void)setName:(NSString *)name {
[[NSUserDefaults standardUserDefaults] setObject:name forKey:@"name"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (NSString *)name {
return [[NSUserDefaults standardUserDefaults] objectForKey:@"name"];
}
如果您想对所有属性使用简单的方法:
- (id)objectForKey:(NSString *)key {
return [[NSUserDefaults standardUserDefaults] objectForKey:key];
}
- (void)setObject:(id)object forKey:(NSString *)key {
[[NSUserDefaults standardUserDefaults] setObject:object forKey:key];
[[NSUserDefaults standardUserDefaults] synchronize];
}
创建许多键,而不是创建许多属性,每个键都是您要保存或检索的键。 密钥示例:
static NSString *const kName = @"name";
static NSString *const kLastName = @"lastName";
答案 1 :(得分:2)
我发现你的问题非常有趣,我对自己说“接受挑战!”。
我在Github上创建了this项目。
基本上,您所要做的就是将VBSettings类子类化,然后声明de属性,如下所示:
findOrFail()
“hello”的值将以“hello”键保存到NSUserDefaults。用法示例:
->where("id", "=", $whatever)->first()
答案 2 :(得分:2)
另一种方法可能就是这样。 没有属性,只是键值下标。
@interface DBObject : NSObject<NSCoding>
+ (instancetype)sharedObject;
@end
@interface NSObject(SubScription)
- (id)objectForKeyedSubscript:(id)key;
- (void)setObject:(id)obj forKeyedSubscript:(id <NSCopying>)key;
@end
在实施文件中:
+ (instancetype)sharedObject {
static DBObject *sharedObject = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedObject = [[DBObject alloc] init];
});
return sharedObject;
}
- (id)objectForKeyedSubscript:(id)key {
return [[NSUserDefaults standardUserDefaults] objectForKey:key];
}
- (void)setObject:(id)obj forKeyedSubscript:(id <NSCopying>)key {
[[NSUserDefaults standardUserDefaults] setObject:obj forKeyedSubscript:key];
[[NSUserDefaults standardUserDefaults] synchronize];
}
现在,您可以像这样使用它:
// You saved it in NSUserDefaults
[DBObject sharedObject][@"name"] = @"John";
// You retrieve it from NSUserDefaults
NSLog(@"Name is: %@", [DBObject sharedObject][@"name"]);
我认为这是最好的方法,也是我将来会使用的方法。
答案 3 :(得分:0)
一种可能性是使用KVO来检测您的属性何时发生变化。
E.g:
@interface Settings : NSObject
@property NSString *one;
@property NSString *two;
@end
@implementation Settings
- (instancetype)init
{
self = [super init];
if (self) {
[self addObserver:self forKeyPath:@"one" options:NSKeyValueObservingOptionNew context:NULL];
[self addObserver:self forKeyPath:@"two" options:NSKeyValueObservingOptionNew context:NULL];
}
return self;
}
- (void)dealloc
{
[self removeObserver:self forKeyPath:@"one"];
[self removeObserver:self forKeyPath:@"two"];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
NSLog(@"Key: %@, Change: %@", keyPath, change);
}
@end
在其他类中,使用标准属性访问:
Settings *settings = [[Settings alloc] init];
settings.one = @"something for one";
设置对象日志:
关键:一,改变:{ kind = 1; new =“for something for one”; }
答案 4 :(得分:0)
您可以尝试使用动态Getter和Setter声明as noted in this answer.
首先创建您希望使用所有属性的泛型函数:
- (id)_getter_
{
return [[NSUserDefaults standardUserDefaults] objectForKey:NSStringFromSelector(_cmd)];
}
- (void)_setter_:(id)value
{
//This one's _cmd name has "set" in it and an upper case first character
//This could take a little work to parse out the parameter name
[[NSUserDefaults standardUserDefaults] setObject:object forKey:YourParsedOutKey];
[[NSUserDefaults standardUserDefaults] synchronize];
}
然后创建动态方法生成器:
+(void)synthesizeForwarder:(NSString*)getterName
{
NSString*setterName=[NSString stringWithFormat:@"set%@%@:",
[[getterName substringToIndex:1] uppercaseString],[getterName substringFromIndex:1]];
Method getter=class_getInstanceMethod(self, @selector(_getter_));
class_addMethod(self, NSSelectorFromString(getterName),
method_getImplementation(getter), method_getTypeEncoding(getter));
Method setter=class_getInstanceMethod(self, @selector(_setter_:));
class_addMethod(self, NSSelectorFromString(setterName),
method_getImplementation(setter), method_getTypeEncoding(setter));
}
然后设置要为其创建动态getter和setter的字符串:
+(void)load
{
for(NSString*selectorName in [NSArray arrayWithObjects:@"name", @"anything", @"else", @"you", @"want",nil]){
[self synthesizeForwarder:selectorName];
}
}
这将为您添加到数组的任何变量名创建getter和setter。我不确定当其他类试图调用这些方法时这会有多好,编译器在编译时不会看到它们,所以当你尝试使用它们时可能会抛出错误。我只是结合了另外两个 StackOverflow会根据您的情况对这一个问题提出疑问。
答案 5 :(得分:0)
据我了解,您不希望此类用户对setObject:forKey:
和objectForKey:
方法调用产生心理开销。
以下是如何绕过它。我给你留下了很多空白。
在头文件中声明属性,以便调用者可以使用它:
@property NSString *something;
@property NSString *somethingElse;
在类文件本身中,声明您正在定义属性,以便编译器不会感到沮丧:
@dynamic something,somethingElse;
在类文件中,实现methodSignatureForSelector
函数,如下所示:
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{if (SelectorIsGetter(aSelector))
return [NSMethodSignature signatureWithObjCTypes:"@@:"];
if (SelectorIsSetter(aSelector))
return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
return nil;
}
这将告诉系统为这些选择器调用forwardInvocation:
,并且还会告诉它正在进行的调用的形状。
SelectorIsGetter
和SelectorIsSetter
的实施取决于您。您可能会使用NSStringFromSelector(aSelector)
来获取选择器的名称,然后在名称表中查找它以查看它是否与您正在实现的选择器的任何名称相匹配:在本例中为something
和获取者为somethingElse
,后者为setSomething:
和setSomethingElse:
。
在类文件中,实现forwardInvocation:
函数,如下所示:
-(void)forwardInvocation:(NSInvocation *)anInvocation
{if (SelectorIsGetter(anInvocation.selector))
{NSString *s=[self objectForKey:NSStringFromSelector(anInvocation.selector)];
[anInvocation setReturnValue:&s];
return;
};
if (SelectorIsSetter(anInvocation.selector))
{NSString *s;
[anInvocation getArgument:&s atIndex:2];
[self setObjectForKey:UnmangleName(NSStringFromSelector(anInvocation.selector))];
return;
};
[super forwardInvocation:anInvocation];
}
...其中UnmangleName
是一个非常繁琐的函数,它接受类似“setSomething:”的字符串并将其转换为类似“某事”的字符串。
如果您想做的不仅仅是NSStrings,那么扩展程序相当简单。