views:

2830

answers:

5

I am using Core Data programmatically (i.e. not using .xcdatamodel data model files) in much the same manner as depicted in Apple's Core Data Utility Tutorial. So my problem is that when I try to add an attribute to an entity with the type NSBooleanAttributeType, it gets a bit buggy. When I add it to my NSManagedObject subclass header file (in the tutorial, that would be Run.h) as

@property (retain) BOOL *booleanProperty;

compiling fails, saying error: property 'booleanProperty' with 'retain' attribute must be of object type.

It seems like some places in Cocoa use NSNumber objects to represent booleans, so I tried setting it to

@property (retain) NSNumber *booleanProperty;

instead. However, this evokes the following runtime errors:

*** -[NSAttributeDescription _setManagedObjectModel:]: unrecognized selector sent to instance 0x101b470
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSAttributeDescription _setManagedObjectModel:]: unrecognized selector sent to instance 0x101b470'

Using GDB, I am able to trace this back to the line in my source code where I add my entity to the managed object model:

[DVManagedObjectModel setEntities:[NSArray arrayWithObjects:myEntityWithABooleanAttribute, myOtherEntity]];

So my question is this: what type should I set booleanProperty to in my custom class header?

+6  A: 

Try:

@property (nonatomic) BOOL booleanProperty;

The problem was that you used the retain in the property definition. For that you must have a property for an Objective-C class (it should be able to understand the 'retain' method). BOOL is not a class but an alias for signed char.

Diederik Hoogenboom
Also worth pointing out that it is “BOOL booleanProperty”, with no *. The * is only for pointers, including object pointers.
Peter Hosey
+3  A: 

I don't know if this is just a typo on your part, but this:

[NSArray arrayWithObjects:myEntityWithABooleanAttribute, myOtherEntity]

is definitely wrong. The last parameter of that method should always be nil.

Mike Abdullah
Thanks for pointing that out! That was a typo though.
zuwiki
+2  A: 

One of the best ways to generate correct accessors in your NSManagedObject subclass is to bring up the contextual menu on a attribute or property in the data modeling tool and choose one of the following commands:

  • Copy Method Declarations to Clipboard
  • Copy Method Implementations to Clipboard
  • Copy Obj-C 2.0 Method Declarations to Clipboard
  • Copy Obj-C 2.0 Method Implementations to Clipboard
Jim Correia
+1  A: 

An attribute of type Boolean in a NSManagedObject is of type NSCFBoolean. This is a private subclass of NSNumber.

A: 

I wouldn't recommend the method suggested by Diederik Hoogenboom (i got an error even though my core data attribute was set as Boolean).

It's worth pointing out that although this line will work for a custom object, it will not work for a subclass of NSManagedObject:

@property (nonatomic) BOOL booleanProperty;

Your property should be set as this:

@property (nonatomic, retain) NSNumber *booleanProperty;

When i copy the method declarations for a Boolean type (using the technique suggested by Jim Correia), the getter and setter are typed as:

NSNumber:-(NSNumber *)booleanProperty; 
-(void)setBooleanProperty:(NSNumber *)value;

...this is what a Boolean property in core data is set as, and you need to validate your property with something like this:

-(BOOL)validateBooleanProperty:(NSNumber **)toValidate error:(NSError **)outError 
{
    int toVal = [*toValidate intValue];

    if ( (toVal < 0) || (toVal > 1) )
    {
        NSString *errorString = NSLocalizedStringFromTable(@"Boolean Property", @"TheObject", @"validation: not YES or NO");
        NSDictionary *userInfoDict = [NSDictionary dictionaryWithObject:errorString forKey:NSLocalizedDescriptionKey];

        NSError *error = [[[NSError alloc] initWithDomain:NSCocoaErrorDomain code:-1 userInfo:userInfoDict] autorelease];
        *outError = error;
        return NO;
    }
    return YES;
}//END

…remember to include the validateBooleanProperty declaration in the header file. The setter and getter methods store and retrieve your property using -(id)primitiveValueForKey:(NSString *)key.

Finally you need to explicitly call the validate method from whatever view controller / app delegate you're setting the object from:

    NSNumber *boolProp = [[[NSNumber alloc] initWithInt :0] autorelease];
    NSError *valError = nil;        
    if ([TheObject validateBooleanProperty:&boolProp error:&valError] == YES)
    {
        [TheObject setBooleanProperty :boolProp];   
    }       
OOP_Master