Ios Objective-C写作通用的getter和setter方法
在我的项目中,我有一个settings类,它的属性带有自定义setter,可以访问Ios Objective-C写作通用的getter和setter方法,ios,nsuserdefaults,getter-setter,Ios,Nsuserdefaults,Getter Setter,在我的项目中,我有一个settings类,它的属性带有自定义setter,可以访问NSUserDefaults,使一切变得更简单。这个想法是设置类具有 @property NSString *name 它具有从NSUserDefaults获取名称值的自定义getter和保存新值的setter。通过这种方式,在整个项目中,我与Settings类交互只是为了管理用户定义的首选项。问题是,编写所有getter和setter(我有大约50个属性)似乎太重复了,我想创建一个setter和一个getter
NSUserDefaults
,使一切变得更简单。这个想法是设置
类具有
@property NSString *name
它具有从NSUserDefaults
获取名称值的自定义getter和保存新值的setter。通过这种方式,在整个项目中,我与Settings类交互只是为了管理用户定义的首选项。问题是,编写所有getter和setter(我有大约50个属性)似乎太重复了,我想创建一个setter和一个getter来处理所有变量。我唯一的问题是如何在setter中获得变量的名称
最后一个问题是:是否有可能在getter或setter中找出调用函数的属性
如果你有其他方法,我也会很感激,但是考虑到我想把所有的NSUserDefaults
东西都放在一个类中,我想不出一个不包括编写50个getter和setter的替代方法
谢谢 本例中的setter和getter很简单,您可以这样做:
- (void)setName:(NSString *)name {
[[NSUserDefaults standardUserDefaults] setObject:name forKey:@"name"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (NSString *)name {
return [[NSUserDefaults standardUserDefaults] objectForKey:@"name"];
}
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{if (SelectorIsGetter(aSelector))
return [NSMethodSignature signatureWithObjCTypes:"@@:"];
if (SelectorIsSetter(aSelector))
return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
return nil;
}
-(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];
}
@interface MySettings : VBSettings
@property (strong, nonatomic) NSString *hello;
@end
// You saved it in NSUserDefaults
[DBObject sharedObject][@"name"] = @"John";
// You retrieve it from NSUserDefaults
NSLog(@"Name is: %@", [DBObject sharedObject][@"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";
一种可能是使用KVO来检测您的属性何时更改 例如: 设置对象日志: 关键:一、改变:{ 种类=1; new=“一个人的东西”}
您可以尝试使用动态Getter和Setter声明 首先创建希望所有属性都使用的通用函数:
- (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。我不确定当其他类尝试调用这些方法时,这种方法的效果如何,编译器在编译时不会看到它们,因此在尝试使用它们时可能会抛出错误。我刚刚加了另外两个
根据您的情况,将问题叠加到这个答案中
据我所知,您不希望此类用户调用
setObject:forKey:
和objectForKey:
方法时产生心理负担
下面是如何绕过它的。我留下了很多空白让你填补
@property NSString *something;
@property NSString *somethingElse;
@dynamic something,somethingElse;
methodSignatureForSelector
函数,如下所示:
- (void)setName:(NSString *)name {
[[NSUserDefaults standardUserDefaults] setObject:name forKey:@"name"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (NSString *)name {
return [[NSUserDefaults standardUserDefaults] objectForKey:@"name"];
}
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{if (SelectorIsGetter(aSelector))
return [NSMethodSignature signatureWithObjCTypes:"@@:"];
if (SelectorIsSetter(aSelector))
return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
return nil;
}
-(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];
}
@interface MySettings : VBSettings
@property (strong, nonatomic) NSString *hello;
@end
// You saved it in NSUserDefaults
[DBObject sharedObject][@"name"] = @"John";
// You retrieve it from NSUserDefaults
NSLog(@"Name is: %@", [DBObject sharedObject][@"name"]);
forwardInvocation:
,还将告诉系统正在进行的调用的形状
selectorisetter
和selectorisetter
的实现取决于您。您可能会使用NSStringFromSelector(aSelector)
获取选择器的名称,然后在名称表中查找它,查看它是否与您正在实现的选择器的任何名称匹配:在本例中,getter的somethinges
和somethingese
和setter的setsomethinges:
forwardInvocation:
函数,如下所示:
- (void)setName:(NSString *)name {
[[NSUserDefaults standardUserDefaults] setObject:name forKey:@"name"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (NSString *)name {
return [[NSUserDefaults standardUserDefaults] objectForKey:@"name"];
}
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{if (SelectorIsGetter(aSelector))
return [NSMethodSignature signatureWithObjCTypes:"@@:"];
if (SelectorIsSetter(aSelector))
return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
return nil;
}
-(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];
}
@interface MySettings : VBSettings
@property (strong, nonatomic) NSString *hello;
@end
// You saved it in NSUserDefaults
[DBObject sharedObject][@"name"] = @"John";
// You retrieve it from NSUserDefaults
NSLog(@"Name is: %@", [DBObject sharedObject][@"name"]);
UnmangleName
是一个非常乏味的函数,它接受类似“setSomething:”的字符串,并将其转换为类似“something”的字符串
如果您想做的不仅仅是nsstring,那么扩展相当简单。我发现您的问题非常有趣,我对自己说:“接受挑战!” 我已经在Github上创建了一个项目 基本上,您只需将VBSettings类划分为子类,然后声明de属性,如下所示:
- (void)setName:(NSString *)name {
[[NSUserDefaults standardUserDefaults] setObject:name forKey:@"name"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (NSString *)name {
return [[NSUserDefaults standardUserDefaults] objectForKey:@"name"];
}
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{if (SelectorIsGetter(aSelector))
return [NSMethodSignature signatureWithObjCTypes:"@@:"];
if (SelectorIsSetter(aSelector))
return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
return nil;
}
-(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];
}
@interface MySettings : VBSettings
@property (strong, nonatomic) NSString *hello;
@end
// You saved it in NSUserDefaults
[DBObject sharedObject][@"name"] = @"John";
// You retrieve it from NSUserDefaults
NSLog(@"Name is: %@", [DBObject sharedObject][@"name"]);
“hello”的值将通过键“hello”保存到NSUserDefaults。用法示例:
MySettings settings = [[MySettings alloc] init];
settings.hello = "World!"; //The value is saved in NSUserDefaults
NSLog(@"%@", settings.hello); //The value is restored from NSUserDefaults.
另一种方法可能是这样。 没有属性,只有键值下标
@interface DBObject : NSObject<NSCoding>
+ (instancetype)sharedObject;
@end
@interface NSObject(SubScription)
- (id)objectForKeyedSubscript:(id)key;
- (void)setObject:(id)obj forKeyedSubscript:(id <NSCopying>)key;
@end
我认为这是最好的方法,也是我将来将使用的方法。50属性?听起来你应该考虑把它分成几个类。谢谢你,它看起来很棒,达到了我所需要的目的。我只想指出,这可能对其他人非常有用,在这种情况下,您可以再添加两个方法:clearAll和registerDefaults:@{}。我肯定打算在用户注销时将这两个函数都实现到子类中,并默认在AppDelegate中轻松可见。再次感谢!非常感谢。我感谢你的反馈。我真的打算在不久的将来改进这个项目,包括你提到的两种方法。但我忘了,“挑战完成了!”)这听起来是一种合理的方法,尽管它显然比vladiulianbogdan提供的类的子类化复杂得多。尽管如此,这是一个好主意,谢谢你,它确实帮助我对可能的实现有了更广泛的了解。看起来不错,但我担心这两个概念的结合。它当然有潜力发挥作用,但必须经过测试。考虑到这一点真是太好了。这是解决问题的一个明智的方法,尽管我一直都有KVO的问题,如果可能的话,我想避免。你是对的,这将起作用,我实际上有一些与你的第一个版本非常相似的东西,但问题是一旦你有了许多属性,你会有很多功能,所以如果出于任何原因我需要更改它,那么维护它将是一件痛苦的事情。这就是为什么我在寻找一个通用选项,这样一个getter和一个setter就可以处理所有属性(这是上面的人设法实现的)