2

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

。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 将处于无限循环中。有没有办法实现两个不会相互循环的私有设置器?

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

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

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

5 回答 5

2

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

// `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"];
}
于 2013-04-03T20:15:08.303 回答
1

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

这是正确的:如果你在 setter 中使用 setter,你会得到一个无限递归。这就是为什么您应该在 setter +中设置实例变量而不是属性的原因。另一种解决方案是检查当前值:如果一个属性被设置为它已经拥有的值,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

于 2013-04-03T20:07:53.887 回答
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";
    }
}
于 2013-04-03T20:08:25.240 回答
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"];
}
于 2013-04-03T20:08:45.670 回答
0

另一种解决方案是检查设置器中的相等值:

- (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"];
    }
}

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

于 2013-04-03T20:21:36.347 回答