Я, как и многие другие программисты до меня, работаю над приложением, которое имеет дело с деньгами. Я' 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 .
Однако, как ни странно, , если я изменю тип этого свойства на 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;
}
Вы сможете воспроизвести эту проблему с помощью следующий пример кода для класса 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
Всем спасибо.
Я сделал еще один тест, и я подозреваю, что это связано с методом 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;
}
Это история о вашей проблеме - я думаю
И помните, что десятичная дробь не дробная - куда бы вы поместили десятичную точку?
У плавающих точек есть это внутри, называемое мантиссой ...