Chr*_*ick 29 xcode cocoa core-data objective-c
我和我之前的许多程序员一样,正致力于处理金钱的应用程序.我对Cocoa编程比较陌生,但在阅读完手册之后,我决定尝试使用Core Data,因为它提供了许多我想要的功能,并且应该让我免于重新发明轮子.无论如何,我的问题与我是否应该使用核心数据没有任何关系:它与Core Data和XCode本身的行为有关.
更新:我向Apple提交了一份错误报告,并被告知它是问题ID 9405079的副本.他们知道这个问题,但我不知道他们何时或是否要修复它.
由于某些我无法理解的原因,当我在托管对象模型中编辑Decimal属性时,XCode会覆盖最小值和最大值约束.(我在这里描述的原因使用了Decimal属性.)
假设我有一个名为Decimal属性的Core Data实体value
(这只是为了说明;我也使用了其他属性名称).我希望它的值大于0,但因为XCode只允许我指定最小值(包括),所以我将Min Value设置为等于0.01
.令我惊讶的是,这导致了验证谓词SELF >= 0
!当我更改最小值时,我得到相同的结果:所有小数值都被截断(最小值被覆盖).最大值具有相同的行为.
通过图示的方式,value
在下面的截图属性将导致验证谓词SELF >= 0
和SELF <= 1
.
但奇怪的是,如果我将此属性的类型更改为Double或Float,则验证谓词将更改为SELF >= 0.5
和SELF <= 1.2
,如预期的那样.更奇怪的是,如果我按照Core Data Utility Tutorial创建自己的数据模型,即使对于十进制属性,验证谓词也会正确设置.
由于我在XCode的托管对象模型编辑器中找不到任何解决此问题的方法,因此我在应用程序委托的方法中添加了以下代码(由begin workaround
和end workaround
comments 指示)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;
}
Run Code Online (Sandbox Code Playgroud)
您应该能够使用以下DebugController
类的示例代码重现此问题,该类将托管对象模型中的每个属性的约束打印到标签.此代码做出以下假设.
DecimalTest_AppDelegate
managedObjectContext
方法请执行以下步骤以使用此代码.
DebugController
在Interface Builder中实例化.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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
感谢大家.
我又做了一次测试,我怀疑这与和 的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");
}
Run Code Online (Sandbox Code Playgroud)
输出是:
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
Run Code Online (Sandbox Code Playgroud)
编辑:以下解决方法最初是我添加到问题中的评论,最终被改编到问题正文中。
使用-(BOOL)validate<key>:(id *)ioValue error:(NSError **)outError
您可以实现接近默认行为的行为(如此处所述)。
例如(摘自问题正文,由 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;
}
Run Code Online (Sandbox Code Playgroud)