Objective-C Custom Getter / Setter

时间:2010-11-10 04:22:22

标签: iphone objective-c core-data

我正在使用以下由Xcode自动生成的NSManagedObject:

@interface Portion :  NSManagedObject  
{
}

@property (nonatomic, retain) NSNumber * volume;

我想创建一个自定义getter / setter,根据用户设置的内容在ml / oz之间进行转换,这样数据库始终存储相同的值,并自动转换为首选单位。我的最新尝试看起来像这样:

#import "Portion.h"
#import "SettingHandler.h"

#define MILLILITERS_PER_OUNCE 29.5735296

@implementation Portion 

@dynamic volume;

- (void) setVolume:(NSNumber *) number {
    if ([SettingHandler getUnitsTypeShort] == @"oz") {
        [self setValue:number forKey:@"volume"];
    } else {
        [self setValue:[NSNumber numberWithFloat:[number floatValue] * MILLILITERS_PER_OUNCE] forKey:@"volume"];
    }
}

- (NSNumber *) volume {
    if ([SettingHandler getUnitsTypeShort] == @"oz") {
        return [self valueForKey:@"volume"];
    } else {
        return [NSNumber numberWithDouble: [[self valueForKey:@"volume"] floatValue] * MILLILITERS_PER_OUNCE];
    }
}

setVolume调用最终调用自身导致无限循环。我猜有办法做到这一点,但我不知道它是什么,有什么想法吗?

4 个答案:

答案 0 :(得分:8)

很抱歉玩魔鬼的拥护者,但IMO,似乎你正试图解决一个值如何以低于某个级别显示给用户的方式(在模型对象本身中;请参阅The Model-View-Controller Design Pattern)。为什么不使用在视图级别工作更多的格式化程序来帮助将原始NSNumber值格式化为将呈现给用户的字符串?

然后,您可以使用可重复使用的类,只要您使用表示卷的数字值。格式化程序将存储“unitsType”值,以便它知道如何正确格式化传入的数字。

我使用现有的格式化程序MDFileSizeFormatter作为起点快速编写了一个版本:

#import <Foundation/Foundation.h>

enum {
    MDVolumeFormatterMetricUnitsType            = 1,
    MDVolumeFormatterOurStupidAmericanUnitsType = 2,
    MDVolumeFormatterDefaultUnitsType = MDVolumeFormatterMetricUnitsType
};

typedef NSUInteger MDVolumeFormatterUnitsType;


@interface MDVolumeFormatter : NSFormatter {
    MDVolumeFormatterUnitsType    unitsType;
    NSNumberFormatter            *numberFormatter;
}
- (id)initWithUnitsType:(MDVolumeFormatterUnitsType)aUnitsType;

@property (assign) MDVolumeFormatterUnitsType unitsType;

@end

然后是.m文件:

#import "MDVolumeFormatter.h"

#define MILLILITERS_PER_OUNCE 29.5735296

@implementation MDVolumeFormatter

@synthesize unitsType;

- (id)init {
    return [self initWithUnitsType:MDVolumeFormatterDefaultUnitsType];
}

- (id)initWithUnitsType:(MDVolumeFormatterUnitsType)aUnitsType {
    if (self = [super init]) {
        numberFormatter = [[NSNumberFormatter alloc] init];
        [numberFormatter setFormat:@"#,###.#"];
        [self setUnitsType:aUnitsType];
    }
    return self;
}

- (void)dealloc {
    [numberFormatter release];
    [super dealloc];
}

- (NSString *)stringForObjectValue:(id)anObject {
    if ([anObject isKindOfClass:[NSNumber class]]) {
        NSString *string = nil;
        if (unitsType == MDVolumeFormatterMetricUnitsType) {
            string = [[numberFormatter stringForObjectValue:
                       [NSNumber numberWithFloat:
                        [(NSNumber *)anObject floatValue] * MILLILITERS_PER_OUNCE]]
                      stringByAppendingString:@" mL"];

        } else {
            string = [[numberFormatter stringForObjectValue:anObject] stringByAppendingString:@" oz"];
        }
        return string;
    }
    return nil;
}

@end

这可能会扩展为对传入值进行测试并自动确定适当的音量单位。例如,如果floatValue为16.0,则可以使用if then logic返回“2.0杯”而不是16盎司的字符串。

答案 1 :(得分:7)

在核心数据编程指南中查看"Custom Attribute and To-One Relationship Accessor Methods"。基本上,您应该使用原始getter / setter方法来访问/更改值并使用KVO通知包装这些调用。

您需要为基元访问器添加声明:

@interface Portion (PrimitiveAccessors)
- (NSNumber *)primitiveVolume;
- (void)setPrimitiveVolume:(NSNumber *)number; 
@end

然后你需要替换每一次:

[self setValue:number forKey:@"volume"];

使用:

[self willChangeValueForKey:@"volume"];
[self setPrimitiveVolume:number];
[self didChangeValueForKey:@"volume"];

并在getter中进行相应的更改。

答案 2 :(得分:2)

请尝试使用[self setPrimitiveValue:number forKey:@"volume"];

答案 3 :(得分:2)

我建议采用另一种方法。决定规范表示 - 盎司或毫升。这就是实际存储的卷。然后声明以下getter / setters:

- (void) setOunces:(double)ounces;
- (double) ounces;

- (void) setMilliliters:(double)milliliters;
- (double*) milliliters;

如果规范体积是毫升,那么:

- (void) setOunces:(double)ounces
{
[self setVolume:[NSNumber numberWithDouble:(ounces* MILLILITERS_PER_OUNCE)]];
}

- (double) ounces
{
return [[self volume] doubleValue]/MILLILITERS_PER_OUNCE;
}

- (void) setMilliliters:(double)milliliters
{
[self setVolume:[NSNumber numberWithDouble:milliliters]];
}

- (double) milliliters
{
return [[self volume] doubleValue];
}