29 votes

Est-ce que XCode Floor "Min Value" et "Max Value" pour Core Data Decimal Attributes?

Arrière-plan

J'ai, comme des dizaines de programmeurs devant moi, je travaille sur une application qui traite avec de l'argent. Je suis relativement nouveau pour le Cacao de la programmation, mais après avoir lu dans les manuels, j'ai décidé que je voudrais essayer d'utiliser de Base de Données, car il fournit un certain nombre de caractéristiques que je veux et devrait me sauver de ré-inventer la roue. De toute façon, ma question n'a rien à faire avec si ou pas, je devrais utiliser la Base de Données: il a à voir avec le comportement de Base de Données et XCode eux-mêmes.

Mise à JOUR: j'ai déposé un rapport de bogue avec de la Pomme et a été informé que c'est un doublon d'ID problème 9405079. Ils sont au courant de la question, mais je n'ai aucune idée de quand ou si ils vont le résoudre.

Le Problème

Pour une raison que je ne comprends pas, XCode étages de la Valeur Min et une Valeur Max de contraintes quand j'ai modifier une propriété Décimale dans mon modèle d'objet managé. (Je suis Décimal à l'aide de propriétés pour les raisons décrites ici.)

Supposons que j'ai une Base de Données d'entité avec une Décimale attribut nommé value (c'est seulement à des fins d'illustration; j'ai utilisé d'autres noms d'attributs, en tant que bien). Je veux qu'il ait une valeur supérieure à 0, mais parce que XCode ne permettez-moi de préciser une valeur minimale (inclus), j'ai mis en Valeur Min égal à 0.01. À ma grande surprise, il en résulte une validation prédicat d' SELF >= 0! J'obtiens le même résultat lorsque je change la valeur minimale: toutes les valeurs fractionnaires sont tronqués (la valeur minimale est parqueté). La valeur maximale a le même comportement.

À titre d'illustration, l' value propriété dans la capture d'écran suivante entraînera la validation des prédicats d' SELF >= 0 et SELF <= 1.

value configured in XCode

Curieusement, même si, si je change le type de cette propriété soit Double ou Float, la validation des prédicats va changer d' SELF >= 0.5 et SELF <= 1.2, comme prévu. Plus étrange encore, si je créer mon propre modèle de données suivant de la Base de Données de l'Utilitaire Tutoriel, la validation des prédicats sont définis correctement , même pour les décimales propriétés.

Originale Solution De Contournement

Depuis, je ne peux pas trouver un moyen de corriger ce problème dans XCode de l'objet géré éditeur de modèle, j'ai ajouté le code suivant, qui est indiqué par l' begin workaround et end workaround commentaires à ma demande du délégué managedObjectModel méthode (c'est la même application délégué que XCode fournit par défaut lorsque vous créez un nouveau projet qui utilise la Base de Données). Notez que je suis l'ajout d'une contrainte de garder l' Transaction de l'entité amount bien supérieur à 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;
}

Questions

  1. Est-ce vraiment un bug dans la façon dont XCode génère de validation des prédicats pour les décimales propriétés dans la gestion des modèles d'objet de Base de Données?
  2. Si oui, est-il une meilleure façon de travailler autour d'elle que celles que j'ai décrites ici?

Repro Code

Vous devriez être en mesure de reproduire cette question avec l'exemple de code suivant pour un DebugController classe, qui imprime les contraintes sur chaque propriété d'un objet de modèle à une étiquette. Ce code repose sur les hypothèses suivantes.

  • Vous avez une application nommé délégué DecimalTest_AppDelegate
  • Votre demande délégué a managedObjectContext méthode
  • Votre modèle objet managé est nommé "porte-monnaie"

Suivez les étapes suivantes pour utiliser ce code.

  1. Instancier l' DebugController dans Interface Builder.
  2. Connectez le contrôleur appDelegate sortie de votre application délégué.
  3. Ajouter un emballage étiquette (NSTextField) à votre interface utilisateur et connectez le contrôleur debugLabel prise à elle.
  4. Ajouter un bouton à votre interface utilisateur et se connecter à son sélecteur du programmateur updateLabel action.
  5. Lancez l'application et appuyez sur le bouton connecté à l' updateLabel action. Cette affiche votre modèle d'objet managé des contraintes propres à l' debugLabel et devrait illustrer le comportement que j'ai décrit ici.

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

Merci à tous.

2voto

Grady Player Points 7823

J'ai fait un autre test, et je soupçonne que cela a à voir avec l' compare: méthode de NSNumber et 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");
    }

La sortie est:

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

Edit: La solution de contournement suivante a été à l'origine d'un commentaire que j'ai ajouté à la question et a finalement été adapté sur la question du corps.

À l'aide de -(BOOL)validate<key>:(id *)ioValue error:(NSError **)outError vous pouvez implémenter le comportement qui est proche du comportement par défaut (comme décrit ici).

Par exemple (pris à partir de la question du corps, écrit par l'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;
}

0voto

Mayoneez Points 186

C'est un plan très long mais avez-vous essayé 1,2 au lieu de 1,2?

0voto

Mike Points 539

C'est inportant: http://citeseer.ist.psu.edu/viewdoc/download;jsessionid=86013D0FEFFA6CD1A626176C5D4EF9E2?doi=10.1.1.102.244&rep=rep1&type=pdf

Une histoire à propos de votre problème je pense

Et n'oubliez pas que la virgule n'est pas de fraction, où placeriez-vous le point décimal ?

Floatingpoints a l'intérieur appelé mantisse ...

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X