【发布时间】:2011-08-27 09:32:02
【问题描述】:
背景
我和我之前的许多程序员一样,正在开发一个处理金钱的应用程序。我对 Cocoa 编程比较陌生,但在阅读了手册后,我决定尝试使用 Core Data,因为它提供了许多我想要的功能,应该让我免于重新发明轮。无论如何,我的问题与我是否应该使用 Core Data 没有任何关系:它与 Core Data 和 XCode 本身的行为有关。
更新:我向 Apple 提交了错误报告,并被告知它与问题 ID 9405079 重复。他们知道这个问题,但我不知道他们何时或是否会去修复它。
问题
由于某种我无法理解的原因,当我在托管对象模型中编辑 Decimal 属性时,XCode 会限制 Min Value 和 Max Value 约束。(我正在为the reasons described here 使用十进制属性。)
假设我有一个核心数据实体,它的 Decimal 属性名为value(这只是为了说明;我也使用了其他属性名称)。我希望它的值大于 0,但因为 XCode 只允许我指定最小值(包括),所以我将 Min Value 设置为等于0.01。令我惊讶的是,这导致了SELF >= 0 的验证谓词!当我更改最小值时,我得到相同的结果:所有小数值都被截断(最小值被下限)。最大值具有相同的行为。
作为说明,以下屏幕截图中的value 属性将产生SELF >= 0 和SELF <= 1 的验证谓词。
不过,奇怪的是,如果我将此属性的类型更改为 Double 或 Float,验证谓词将更改为 @正如预期的那样,987654333@ 和 SELF <= 1.2。更奇怪的是,如果我按照Core Data Utility Tutorial 创建自己的数据模型,验证谓词设置正确即使对于十进制属性。
原始解决方法
由于我在 XCode 的托管对象模型编辑器中找不到解决此问题的任何方法,我已将以下代码(由 begin workaround 和 end workaround cmets 表示)添加到我的应用程序委托的 managedObjectModel 方法中(这与 XCode 在您创建使用 Core Data 的新项目时默认提供的应用程序委托相同)。请注意,我添加了一个约束以保持Transaction 实体的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 的托管对象模型中的小数属性生成验证谓词的错误吗?
- 如果是这样,有没有比我在此处描述的方法更好的解决方法?
复制代码
您应该能够使用DebugController 类的以下示例代码重现此问题,该类将托管对象模型中每个属性的约束打印到标签。这段代码做了以下假设。
- 您有一个名为
DecimalTest_AppDelegate的应用程序委托 - 您的应用程序委托有一个
managedObjectContext方法 - 您的托管对象模型名为“Wallet”
按照以下步骤使用此代码。
- 在 Interface Builder 中实例化
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
谢谢大家。
【问题讨论】:
-
是的,我验证了行为,但我找不到任何表明它被错误地保存在 .xcdatamodeld/entities 文件中的东西,所以我怀疑它不是真正的 xcode正在做。可能是运行时环境,也许您应该提交错误报告。您需要使用 Decimal 类型是否有特殊原因?
-
@Grady 谢谢,我很高兴我不是唯一遇到此问题的人。为了回答您的问题,我使用 Decimal 数字来表示它们提供的精度(以 10 为底的数学而不是浮点精度),如前所述 on Stack Overflow and elsewhere。
-
我猜你只需要实现你自己的 -(BOOL)validate
:(id *)ioValue error:(NSError **)outError,将最接近默认行为。跨度> -
@Grady 感谢您的建议!我刚刚更新了我的问题以包含您的解决方法。我实际上曾考虑过提交错误报告,但我不确定在哪里和/或如何提交。你能指出我正确的方向吗?
标签: objective-c xcode cocoa core-data