Objective-C Custom Getter/Setter

Win*_*der 3 iphone core-data objective-c

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

@interface Portion :  NSManagedObject  
{
}

@property (nonatomic, retain) NSNumber * volume;
Run Code Online (Sandbox Code Playgroud)

我想创建一个自定义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];
    }
}
Run Code Online (Sandbox Code Playgroud)

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

NSG*_*God 8

很抱歉玩魔鬼的拥护者,但IMO,似乎你正在尝试解决一个值如何以低于某个级别的方式向用户显示(在模型对象本身中;请参阅模型 - 视图 - 控制器设计模式).为什么不使用在视图级别工作更多的格式化程序来帮助将原始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
Run Code Online (Sandbox Code Playgroud)

然后是.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
Run Code Online (Sandbox Code Playgroud)

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


Tim*_*tis 7

查看"核心数据编程指南"中的"自定义属性和一对一关系访问器方法".基本上,您应该使用原始getter/setter方法来访问/更改值并使用KVO通知包装这些调用.

您需要为原始访问器添加声明:

@interface Portion (PrimitiveAccessors)
- (NSNumber *)primitiveVolume;
- (void)setPrimitiveVolume:(NSNumber *)number; 
@end
Run Code Online (Sandbox Code Playgroud)

然后你需要替换每次出现的:

[self setValue:number forKey:@"volume"];
Run Code Online (Sandbox Code Playgroud)

有:

[self willChangeValueForKey:@"volume"];
[self setPrimitiveVolume:number];
[self didChangeValueForKey:@"volume"];
Run Code Online (Sandbox Code Playgroud)

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