解决两个相互影响的方法

时间:2013-04-03 20:03:14

标签: ios cocoa ios5 getter-setter

我有一个自定义类,包含性别字符串和服务器交互的bool值。

·H

@interface Member : NSObject
...
@property (nonatomic, strong) NSString *gender;
@property (nonatomic) BOOL isMale;
...

的.m

    - (void)setIsMale:(BOOL)isMale
    {
        _isMale = isMale;

        if (isMale) {
            self.gender = @"Male";
        }
        else {
            self.gender = @"Female";
        }
    }

此外,我想做相反的事情,这意味着当性别变为女性时,我希望我的isMale属性设置为NO。但是如果为性别定义一个setter,那么这两个setter将处于无限循环中。有没有办法实现那两个不会互相循环的私人制定者?

这将是导致infitine循环的代码

- (void)setGender:(NSString *)gender
{
    _gender = gender;

    if ([gender isEqualToString:@"Male"]) {
        self.isMale = YES;
    }
    else if ([gender isEqualToString:@"Female"]) {
        self.isMale = NO;
    }
    else {
        // discard...
    }
}

5 个答案:

答案 0 :(得分:2)

如果不在ivar中存储isMale怎么办?这就是我要做的事情:

// `gender` is auto-synthesized

- (BOOL)isMale {
    return [self.gender isEqualToString:@"male"];
}

- (void)setIsMale:(BOOL)isMale {
    self.gender = @"male";
}


// In case you use KVO
+ (NSSet *)keyPathsAffectingValueForIsMale {
    return [NSSet setWithObject:@"gender"];
}

答案 1 :(得分:1)

  

这将是导致无限循环的代码

这是正确的:如果你在setter中使用setter,你将获得无限递归。这就是为什么你应该在seters + 中设置实例变量而不是属性。另一个解决方案是检查当前值:如果将属性设置为它已经存在的值,则setter应该退出。

但是,在您的情况下,问题应该以不同的方式解决:您应该存储一个属性,而不是存储两个属性,然后计算另一个属性。保留isMale,并为gender属性提供getter和setter:

-(NSString*)gender {
    return _isMale ? @"Male" : @"Female";
}
-(void)setGender:(NSString*)gender {
    if ([gender isEqualToString@"Male"]) {
        _isMale = YES;
    } else if ([gender isEqualToString@"Female"]) {
        _isMale = NO;
    } else {
        // Error
    }
}

这是面向对象设计(实际上,任何数据设计)的一个非常重要的原则:你不应该存储可以“动态”轻松计算的东西。典型示例是birthday + current_age对:您应该存储birthday,并计算current_age以避免不一致。

<小时/> + 这在ARC之前通常不正确,即使使用ARC,您也可能希望将setter用于标记为copy的字符串属性。

答案 2 :(得分:1)

第三个笑的原则,或“Divide et impera”:

- (void)setIsMale:(BOOL)male
{
    [self setIsMaleInternal:male];
}

- (void)setGender:(NSString *)_gen
{
    if ([_gen isEqualToString:@"male"]) {
        [self setIsMaleInternal:YES];
    } else if ([_gen isEqualToString:@"female"]) {
        [self setIsMaleInternal:NO];
    } else {
        [World burn]; // or just an exception?
    }
}

- (void)setIsMaleInternal:(BOOL)flag
{
    _isMale = flag;
    if (flag) {
        _gender = @"male";
    } else {
        _gender = @"female";
    }
}

答案 3 :(得分:0)

解决方案是在每个setter中直接引用iVars ......

- (void)setIsMale:(BOOL)isMale
{
    _isMale = isMale;

    if (isMale) {
        _gender = @"Male";
    }
    else {
        _gender = @"Female";
    }
}

但是,由于isMale基本上是派生的,你甚至需要一个属性吗?你能公开一个isMale方法吗?

- (BOOL) isMale {
   return [self.gender isEqualToString:@"Male"];
}

答案 4 :(得分:0)

另一个解决方案是检查setter中的相等值:

- (void)setIsMale:(BOOL)isMale {
    if (_isMale != isMale) {
        _isMale = isMale;

        self.gender = (isMale? @"male" : @"female");
    }
}

- (void)setGender:(NSString *)gender {
    // this is not perfect, you have to take nils into account
    if ([_gender isEqualToString:gender]) {
        _gender = gender;

        self.isMale = [_gender isEqualToString:@"male"];
    }
}

将两个值设置一次后,条件将失败,然后打破无限递归。