Objective-C自定义Getter/Setter

4

我正在使用由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调用会导致其自身被调用,从而导致无限循环。我猜应该有一种方法可以解决这个问题,但我不知道是什么方法,有什么想法吗?


2
嘘,你可能想表达的是 [@"oz" isEqualToString:[SettingHandler getUnitsTypeShort]] 而不是 [SettingHandler getUnitsTypeShort] == @"oz" - justin
4个回答

8
抱歉打扰一下,但在我看来,您似乎试图在太低的层级(模型对象本身;请参见模型-视图-控制器设计模式)上解决如何向用户显示值的问题。为什么不使用更适合在视图层级上工作的格式化程序,以帮助将原始NSNumber值格式化为将呈现给用户的字符串?
然后,您将拥有可重用的类,可以在任何表示音量的数字值中使用该类。格式化程序将存储“单位类型”值,因此它将知道如何正确格式化传入的数字。
我通过使用我现有的格式化程序之一“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逻辑返回一个字符串"2.0杯"而不是16盎司。

1
大约一百万。这绝对是迄今为止发布的问题的最佳解决方案。当然,您还需要一个numberFromString:实现。 - JeremyP

7
请查看Core Data编程指南中的"自定义属性和一对一关系访问器方法"。基本上,您应该使用原始的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

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

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

2
尝试使用[self setPrimitiveValue:number forKey:@"volume"];代替。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接