Есть ли в XCode Floor "Минимальное значение" и "Максимальное значение" для десятичных атрибутов базовых данных? - PullRequest
29 голосов
/ 17 мая 2011

Фон

Я, как и многие программисты до меня, работаю над приложением, которое имеет дело с деньгами.Я относительно новичок в программировании Какао, но после прочтения руководств я решил, что попытаюсь использовать Core Data, потому что он предоставляет ряд функций, которые мне нужны, и должен уберечь меня от повторного изобретениярулевое колесо.В любом случае, мой вопрос не имеет никакого отношения к тому, должен ли я использовать Core Data: он касается поведения Core Data и самого XCode.

ОБНОВЛЕНИЕ: Iподал в Apple отчет об ошибке и был проинформирован о том, что это дубликат идентификатора проблемы 9405079. Они знают об этой проблеме, но я понятия не имею, когда или если они собираются ее исправить.

Проблема

По какой-то причине, которую я не могу понять, XCode устанавливает ограничения Минимальное значение и Максимальное значение при редактировании свойства Decimal в моей модели управляемого объекта. (я использую десятичные свойства для причин, описанных здесь .)

Предположим, что у меня есть объект Core Data с атрибутом Decimal с именем value (это просто для иллюстрацииЯ также использовал другие имена атрибутов).Я хочу, чтобы оно имело значение больше 0, но поскольку XCode позволяет мне указывать только минимальное значение (включительно), я устанавливаю Min Value равным 0.01.К моему большому удивлению, это приводит к предикату проверки SELF >= 0!Я получаю тот же результат, когда я изменяю минимальное значение: все дробные значения усекаются (минимальное значение задается).Максимальное значение имеет такое же поведение.

В качестве иллюстрации, свойство value на следующем снимке экрана приведет к предикатам проверки SELF >= 0 и SELF <= 1.

value configured in XCode

Как ни странно, но , если я изменю тип этого свойства на Double или Float , предикаты проверки изменятся наSELF >= 0.5 и SELF <= 1.2, как и ожидалось.Еще более странно, что если я создам свою собственную модель данных в соответствии с Базовым учебным пособием по основным данным , предикаты проверки установлены правильно даже для десятичных свойств .

Оригинальный обходной путь

Поскольку я не могу найти способ исправить эту проблему в редакторе управляемых объектных моделей XCode, я добавил следующий код, обозначенный комментариями begin workaround и end workaround, к моемуметод managedObjectModel делегата приложения (это тот же делегат приложения, который XCode предоставляет по умолчанию при создании нового проекта, использующего базовые данные).Обратите внимание, что я добавляю ограничение, чтобы свойство Transaction объекта *1059* было больше 0.

- (NSManagedObjectModel *)managedObjectModel {

    if (managedObjectModel) return managedObjectModel;

    managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain];

    // begin workaround
    NSEntityDescription *transactionEntity = [[managedObjectModel entitiesByName] objectForKey:@"Transaction"];
    NSAttributeDescription *amountAttribute = [[transactionEntity attributesByName] objectForKey:@"amount"];
    [amountAttribute setValidationPredicates:[NSArray arrayWithObject:[NSPredicate predicateWithFormat:@"SELF > 0"]]
                      withValidationWarnings:[NSArray arrayWithObject:@"amount is not greater than 0"]];
    // end workaround

    return managedObjectModel;
}

Вопросы

  1. Это действительно ошибка в том, как XCode генерируетпредикаты проверки для десятичных свойств в моделях управляемых объектов для базовых данных?
  2. Если да, то есть ли лучший способ обойти это, чем те, которые я описал здесь?

Код Repro

Вы должны быть в состоянии воспроизвести эту проблему с помощью следующего примера кода для класса DebugController, который выводит ограничения на каждое свойство в модели управляемого объекта на метку.Этот код делает следующие предположения.

  • У вас есть делегат приложения с именем DecimalTest_AppDelegate
  • У вашего делегата приложения есть метод managedObjectContext
  • Ваша модель управляемых объектовимеет имя «Кошелек»

Для использования этого кода выполните следующие действия.

  1. Создание DebugController в Интерфейсном Разработчике.
  2. Подключите контроллерappDelegate розетка для вашего делегата приложения.
  3. Добавьте оберточную метку (NSTextField) к вашему пользовательскому интерфейсу и подключите к ней розетку debugLabel контроллера.
  4. Добавьте кнопку к вашему интерфейсупользовательский интерфейс и подключите его селектор к действию updateLabel контроллера.
  5. Запустите приложение и нажмите кнопку, связанную с действием updateLabel.Это выводит ограничения модели управляемого объекта на debugLabel и должно иллюстрировать поведение, которое я здесь описал.

DebugController.h

#import <Cocoa/Cocoa.h>
// TODO: Replace 'DecimalTest_AppDelegate' with the name of your application delegate
#import "DecimalTest_AppDelegate.h"


@interface DebugController : NSObject {

    NSManagedObjectContext *context;

    // TODO: Replace 'DecimalTest_AppDelegate' with the name of your application delegate
    IBOutlet DecimalTest_AppDelegate *appDelegate;
    IBOutlet NSTextField *debugLabel;

}

@property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext;

- (IBAction)updateLabel:sender;

@end

DebugController.m

#import "DebugController.h"

@implementation DebugController

- (NSManagedObjectContext *)managedObjectContext
{
    if (context == nil)
    {
        context = [[NSManagedObjectContext alloc] init];
        [context setPersistentStoreCoordinator:[[appDelegate managedObjectContext] persistentStoreCoordinator]];
    }
    return context;     
}

- (IBAction)updateLabel:sender
{
    NSString *debugString = @"";

    // TODO: Replace 'Wallet' with the name of your managed object model
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Wallet" inManagedObjectContext:[self managedObjectContext]];
    NSArray *properties = [entity properties];

    for (NSAttributeDescription *attribute in properties)
    {
        debugString = [debugString stringByAppendingFormat:@"\n%@: \n", [attribute name]];
        NSArray *validationPredicates = [attribute validationPredicates];
        for (NSPredicate *predicate in validationPredicates)
        {
            debugString = [debugString stringByAppendingFormat:@"%@\n", [predicate predicateFormat]];
        }
    }
    //  NSPredicate *validationPredicate = [validationPredicates objectAtIndex:1];
    [debugLabel setStringValue:debugString];
}

@end

Спасибо всем.

Ответы [ 3 ]

2 голосов
/ 09 июня 2011

Я провел еще один тест, и я подозреваю, что он связан с методом compare: NSNumber и NSDecimalNumber.

NSDecimalNumber * dn = [NSDecimalNumber decimalNumberWithString:@"1.2"];

if ([dn compare:[NSNumber numberWithFloat:1.2]]==NSOrderedSame) {
        NSLog(@"1.2==1.2");
    }else{
        NSLog(@"1.2!=1.2");
    }

    if ([[NSNumber numberWithFloat:1.2] compare:dn]==NSOrderedSame) {
        NSLog(@"1.2==1.2");
    }else{
        NSLog(@"1.2!=1.2");
    }

Вывод:

2011-06-08 14:39:27.835 decimalTest[3335:903] 1.2==1.2
2011-06-08 14:39:27.836 decimalTest[3335:903] 1.2!=1.2

Редактировать: Следующий обходной путь изначально был комментарием, который я добавил к вопросу и в конечном итоге был адаптирован в теле вопроса.

Используя -(BOOL)validate<key>:(id *)ioValue error:(NSError **)outError, вы можете реализовать поведение, близкое кповедение по умолчанию (как описано здесь ).

Например (взято из тела вопроса, написанного OP Chris):

-(BOOL)validateAmount:(id *)ioValue error:(NSError **)outError {

    // Assuming that this is a required property...
    if (*ioValue == nil)
    {
        return NO;
    }

    if ([*ioValue floatValue] <= 0.0)
    {
        if (outError != NULL)
        {
            NSString *errorString = NSLocalizedStringFromTable(
                @"Amount must greater than zero", @"Transaction",
                @"validation: zero amount error");

            NSDictionary *userInfoDict = [NSDictionary dictionaryWithObject:errorString
                forKey:NSLocalizedDescriptionKey];

            // Assume that we've already defined TRANSACTION_ERROR_DOMAIN and TRANSACTION_INVALID_AMOUNT_CODE
            NSError *error = [[[NSError alloc] initWithDomain:TRANSACTION_ERROR_DOMAIN
                code:TRANSACTION_INVALID_AMOUNT_CODE
                userInfo:userInfoDict] autorelease];
            *outError = error;
        }

        return NO;
    }



  return YES;
}
0 голосов
/ 06 июня 2011

Это важно: http://citeseer.ist.psu.edu/viewdoc/download;jsessionid=86013D0FEFFA6CD1A626176C5D4EF9E2?doi=10.1.1.102.244&rep=rep1&type=pdf

Это история о вашей проблеме - я думаю

И помните, что десятичная дробь не дробная - где бы вы поставили десятичную точку?

У плавающих точек это внутри называется мантисса ...

0 голосов
/ 27 мая 2011

Это очень длинный выстрел, но вы пробовали 1,2 вместо 1,2?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...