Есть ли в XCode Floor «Минимальное значение» и «Максимальное значение» для десятичных атрибутов основных данных?

Предпосылки

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

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

Проблема

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

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

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

value configured in XCode

Однако, как ни странно, , если я изменю тип этого свойства на Double или Float , предикаты проверки изменятся на SELF> = 0,5 и SELF , как и ожидалось. Еще более странно то, что если я создам свою собственную модель данных в соответствии с Core Data Utility Tutorial , предикаты проверки будут установлены правильно даже для десятичных свойств .

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

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

Repro Code

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

DebugController.h

#import 
// 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

Всем спасибо.

29
задан Community 23 May 2017 в 12:19
поделиться

2 ответа

Я сделал еще один тест, и я подозреваю, что это связано с методом 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
ответ дан 28 November 2019 в 02:11
поделиться

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

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

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

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

0
ответ дан 28 November 2019 в 02:11
поделиться