Подтвердить что ты не робот

Значит ли XCode "минимальное значение" и "максимальное значение" для базовых атрибутов ключевых данных?

Фон

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

ОБНОВЛЕНИЕ: Я отправил отчет об ошибке в 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

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

Исходное обходное решение

Поскольку я не могу найти какой-либо способ исправить эту проблему в редакторе объектной модели, управляемой XCode, я добавил следующий код, обозначенный комментариями begin workaround и end workaround, для моего метода делегирования приложения managedObjectModel (например, это тот же делегат приложения, который XCode предоставляет по умолчанию при создании нового проекта, который использует Core Data). Обратите внимание, что я добавляю ограничение, чтобы свойство Transaction entity amount больше 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;
}

Вопросы

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

Код Repro

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

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

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

  • Создайте экземпляр DebugController в построителе интерфейсов.
  • Подключите контроллер appDelegate к делегату приложения.
  • Добавьте в свой пользовательский интерфейс метку упаковки (NSTextField) и подключите к ней выход контроллера debugLabel.
  • Добавьте кнопку в свой пользовательский интерфейс и подключите ее селектор к действию контроллера updateLabel.
  • Запустите приложение и нажмите кнопку, подключенную к действию 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

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

4b9b3361

Ответ 1

Я сделал еще один тест, и я подозреваю, что он связан с методом 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, вы можете реализовать поведение, близкое к поведению по умолчанию (как описано ).

Например (взято из органа вопроса, написанного О. П. Крисом):

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

Ответ 2

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