我正在使用以下由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调用最终调用自身导致无限循环。我猜有办法做到这一点,但我不知道它是什么,有什么想法吗?
答案 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];
}