Фон
Я, как и множество программистов передо мной, работаю над приложением, которое занимается деньгами. Я относительно новичок в программировании 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
.
Как ни странно, если я изменил тип этого свойства на Двойной или 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
Спасибо всем.