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
.
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
- 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?
- 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.
- Instancier l'
DebugController
dans Interface Builder. - Connectez le contrôleur
appDelegate
sortie de votre application délégué. - Ajouter un emballage étiquette (
NSTextField
) à votre interface utilisateur et connectez le contrôleurdebugLabel
prise à elle. - Ajouter un bouton à votre interface utilisateur et se connecter à son sélecteur du programmateur
updateLabel
action. - 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.