NSExpression with constant NSData value crashes Core Data
NSExpression with constant NSData value crashes Core Data
- Subject: NSExpression with constant NSData value crashes Core Data
- From: Jerry Krinock <email@hidden>
- Date: Fri, 22 May 2009 11:05:43 -0700
On 2009 May 17, at 15:10, Ben Trumbull wrote:
Core Data supports == and != searches against binary data. You
should be able to just use a predicate like:
[NSPredicate predicateWithFormat:@"myTransformableAttribute = %@",
myGuidObject]
and have it "just work".
Read the above carefully!
myGuidObject is an NSData, right? So when you make that predicate
with %@, it becomes a string description, complete with angle brackets
and a whitespace separating each group the four bytes. Core Data is
actually comparing the ^descriptions^ of NSData objects. Yuck!!
Months ago, I wrote code with using -[NSComparisonPredicate
predicateWithLeftExpression:rightExpression:modifier:type:options:].
I used -[NSExpression expressionWithConstantValue:] to create the
expressions. The left expression value was the name of an attribute
which is of type "Binary Data", and the right expression value was an
NSData object. This seemed to be reasonable because -[NSExpression
expressionForConstantValue:] takes an id as a parameter.
Result: It worked fine with the XML store (Why??). But when I switch
to the SQLite store, it crashes when Core Data sends a -UTF8String
message to the data object -- because it's expecting a damned
description string. Took me several hours before I finally read Ben's
post very carefully and figured out why it was doing that.
Did I miss some documentation somewhere, or is this kind of a bug?
To work around, I now use the following method in place of -
expressionForConstantValue, and my app is happy. Does this make sense?
@implementation NSExpression (NSDataSupport)
+ (NSExpression*)betterExpressionForConstantValue:(id)value {
if (![value respondsToSelector:@selector(UTF8String)]) {
value = [value description] ;
}
return [self expressionForConstantValue:value] ;
}
Thanks,
Jerry Krinock
P.S. If anyone would like a little demo of how NSExpression + NSData
works fine with the XML store and fails with the SQLite store, here ya
go.....
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
@interface NSData (DebugBadMessage)
- (char*)UTF8String ;
@end
@implementation NSData (DebugBadMessage)
- (char*)UTF8String {
NSLog(@"\n Core Data has just sent -UTF8String to NSData %@\n"
" In real life, you'd crash here, but we've implemented\n"
" -UTFString so you can get away with it in this demo.",
self) ;
return NULL ;
}
@end
// Note: This function returns a retained, not autoreleased, instance.
NSManagedObjectModel *getStaticManagedObjectModel() {
static NSManagedObjectModel *mom = nil;
if (mom != nil) {
return mom;
}
mom = [[NSManagedObjectModel alloc] init];
NSEntityDescription *fooEntity = [[NSEntityDescription alloc]
init];
[fooEntity setName:@"Foo"];
[fooEntity setManagedObjectClassName:@"Foo"];
[mom setEntities:[NSArray arrayWithObject:fooEntity]];
NSMutableArray* properties = [[NSMutableArray alloc] init] ;
NSAttributeDescription *attributeDescription;
attributeDescription = [[NSAttributeDescription alloc] init];
[attributeDescription setName:@"data"];
[attributeDescription setAttributeType:NSBinaryDataAttributeType];
[attributeDescription setOptional:YES];
[properties addObject:attributeDescription] ;
[attributeDescription release] ;
[fooEntity setProperties:properties];
[properties release] ;
[fooEntity release] ;
return mom;
}
NSManagedObjectContext *CreateManagedObjectContext(NSString*
storeType) {
NSLog(@"\n\n***** Creating moc with type %@ *****", storeType) ;
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc]
init];
NSPersistentStoreCoordinator *coordinator =
[[NSPersistentStoreCoordinator alloc]
initWithManagedObjectModel: getStaticManagedObjectModel()];
[moc setPersistentStoreCoordinator: coordinator];
[coordinator release] ;
NSString* path ;
path = [NSHomeDirectory() stringByAppendingPathComponent:@"Junk"] ;
NSError *error;
NSURL* storeUrl = [NSURL fileURLWithPath:path] ;
NSPersistentStore *newStore ;
newStore = [coordinator addPersistentStoreWithType:storeType
configuration:nil
URL:storeUrl
options:nil
error:&error];
if (newStore == nil) {
NSLog(@"Store Configuration Failure\n%@",
([error localizedDescription] != nil) ?
[error localizedDescription] : @"Unknown Error");
}
return moc;
}
void TestFetch(NSString* storeType) {
NSManagedObjectContext *moc =
CreateManagedObjectContext(storeType);
// Create the data we'll use to test our predicate.
// Any data will do.
NSData* data = [@"Hello" dataUsingEncoding:NSUTF8StringEncoding] ;
#define ADD_AN_OBJECT 1
// The following section is optional. The problem still
// occurs whether there is an object in the store or not.
#ifdef ADD_AN_OBJECT
// Insert a Foo
NSManagedObject* foo ;
foo = [NSEntityDescription insertNewObjectForEntityForName:@"Foo"
inManagedObjectContext:moc] ;
// And the following statement is optional too.
// In other words, it doesn't matter whether the object is
// in the store matches the predicate or not.
[foo setValue:data
forKey:@"data"] ;
#endif
// Create a fetch request
NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityForName:@"Foo"
inManagedObjectContext:moc]];
NSExpression* lhs = [NSExpression expressionForKeyPath:@"data"] ;
NSExpression* rhs = [NSExpression
expressionForConstantValue:data] ;
NSPredicate* predicate ;
predicate = [NSComparisonPredicate predicateWithLeftExpression:lhs
rightExpression:rhs
modifier:NSDirectPredicateModifier
type:NSEqualToPredicateOperatorType
options:0] ;
NSLog(@"Here's the predicate:\n%@", predicate) ;
[fetchRequest setPredicate:predicate] ;
// Execute the fetch request
NSLog(@"Executing fetch request...") ;
NSArray* fetches = [moc executeFetchRequest:fetchRequest
error:NULL] ;
NSLog(@"Fetched returned %d objects", [fetches count]) ;
[moc release] ;
}
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSManagedObjectModel *mom = getStaticManagedObjectModel();
TestFetch(NSXMLStoreType) ;
TestFetch(NSSQLiteStoreType) ;
[mom release] ;
[pool release] ;
return 0;
}
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden