Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/mercurial/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios Objective-C写作通用的getter和setter方法_Ios_Nsuserdefaults_Getter Setter - Fatal编程技术网

Ios Objective-C写作通用的getter和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

在我的项目中,我有一个settings类,它的属性带有自定义setter,可以访问
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就可以处理所有属性(这是上面的人设法实现的)