我有一个自定义类,包含性别字符串和服务器交互的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...
}
}
答案 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"];
}
}
将两个值设置一次后,条件将失败,然后打破无限递归。