Implementing Transient Properties - iphone

I am adding a transient property to my Core Data-based app, and I think I am missing something. In the Data Model Editor, I added a optional, transient, BOOL property called isUnderwater.
In my model's header file, I added: #property (nonatomic) BOOL isUnderwater;, then I implemented these methods in the implementation file:
- (BOOL)isUnderwater {
... implementation ...
return (ret);
}
- (void)setIsUnderwater:(BOOL)isUnderwater {}
But when I try to use isUnderwater as a condition in a NSPredicate, I get this error: *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'keypath isUnderwater not found in entity <NSSQLEntity Wheel id=2>'.
Any ideas?
Thanks!

First, you can't use a transient property in a NSFetchRequest that is going against a SQLite store. When you are using SQLite the NSFetchRequest is translated into sql and run against the database, long before your transient is ever touched.
Also, you should not be implementing the accessors, you should be using #synthesize instead.
Next, if you are wanting to set the transient property then you should be setting it in the -awakeFromFetch and/or -awakeFromInsert instead of overriding the getter.
Next, your property should be called underwater and the #property definition should be:
#property (nonatomic, retain, getter=isUnderwater) NSNumber *underwater;
Note: even though you are declaring it a boolean in your model, it is still a NSNumber in code.
Lastly, setting the optional flag on a transient property has no value since it will be thrown away anyway.
Update
You can apply additional filters (even against transient properties) once the entities are in memory. The only limitation is that you can't use transient properties when you are going out to the SQLite file.
For example, you could perform a NSFetchRequest that loads in all of the entities. You could then immediately apply a second NSPredicate against the returned NSArray and further filter the objects down.

Related

Objective-C property access: unrecognized selector sent to instance

I am new to objective-c, but coding for many years now. Somehow I don't get warm with Objective-C. I searched google and stackoverflow, but I think my problem is just to simple and stupid that no one has asked this yet.
My Code is based on DateSelectionTitles Example. http://developer.apple.com/library/ios/#samplecode/DateSectionTitles/Introduction/Intro.html
I have an NSManagedObject
#interface Event : NSManagedObject
#property (nonatomic, retain) NSDate * date;
...
// Cache
#property (nonatomic, retain) NSString * primitiveSectionIdentifier;
All prperties are defined in my datamodel, except the primitiveSectionIdentifier (as in the apple example)
But when I call
NSString *tmp = [self primitiveSectionIdentifier];
I get the Exception
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Event primitiveSectionIdentifier]: unrecognized selector sent to instance 0x74850c0'
To put it simple:
Event *foo = [[Event alloc] init];
if (foo.primitiveSectionIdentifier) {
NSLog(#"YEAH");
}
throws the same exception. So I basically want to check if primitiveSectionIdentifier is nil. But when I access the property, it throws an exception? Do I need to alloc each property before I can check if it has a value?!
Which of the Objective-C basics am I not getting here?
Thanks a lot for responses!
There is only one way how this can happen without compiler warnings - you must have written #dynamic primitiveSectionIdentifier; in your implementation file. This means that you don't want to define the method because you believe it is already defined somewhere else.
You are using a NSManagedObject, do you know how it works? You declare methods without implementation (putting #dynamic in the implementation) and when the method is called, it is not found and a special handler [NSObject doesNotRecognizeSelector:] is called instead. This handler checks the Core Data model whether an attribute for the given selector exists and if it doesn't, it throws the exception you are seeing.
So, the problem might the caused by the fact that primitiveSectionIdentifier is not declared in your model.
You are using an older example program, which uses a different style of memory management; if you are compiling under the iOS 5 or 6, that may be causing the problem.
Try
NSLog(#"primitiveSectionIdentifier = %#", self.primitiveSectionIdentifier);
If it doesn't give you the string you are looking for then the problem is likely in that the string object was never initialized and is still set to nil. In that situation, the code would compile, but sending a selector to a nil pointer would throw up an exception.

Mutable sets in NSManagedObjects?

The standard declaration for an autogenerated NSManagedObject property declares to-many relationships like this:
#property (nonatomic, retain) NSSet *somethings;
...
#dynamic somethings;
Would it be safe to change that declaration to an NSMutableSet? Would the CoreData component still function correctly?
Judging by the #dynamic, it probaby doesn't care if I use an NSSet subclass rather than an NSSet, but I don't want to rewrite a lot of code only to find out it doesn't work.
According to Apple's Core Data Programming Guide, this should always be declared as a NSSet.
If you want a mutable proxy (which is basically the mutable version of the core data set and works exactly the same) then you would use the function mutableSetValueForKey like this:
// myManagedObject is the managed object that has the somethings property.
NSMutableSet *mutableSomethings = [myManagedObject mutableSetValueForKey:#"somethings"];
mutableSomethings can then be modified as a standard NSMutableSet andsomethings will be updated and KVO methods will be appropriately called.
Note however, that many features of the mutable set (such as addSomethingsObject and removeSomethingsObject) are already provided in the core data generated accessors so in many cases you don't need to use the proxy.
You should keep it as an NSSet and do one of the following:
Use key value coding
Add the core data generated accessors
For key value coding, you'll access your collection like so:
NSMutableSet *somethings = [object mutableSetValueForKey:#"somethings"];
[somethings addObject:newObject];
For core data generated accessors, you'd add the following to your header:
#interface MyManagedObject (CoreDataGenerated)
- (void)addSomethingsObject:(MySomething *)object;
- (void)removeSomethingsObject:(MySomething *)object;
- (void)addSomethings:(NSSet *)somethings;
- (void)removeSomethings:(NSSet *)somethings;
#end
You do not need to implement these methods (Core Data will take care of it for you), and you can call them easily. This is my preferred way of handling collections because you get better type checking from the compiler.
The reason you should not change it to an NSMutableSet is because there is a good chance that you will cause crashes and/or that your changes will not be persisted into your Core Data store.
You may also want to look into mogenerator to help you out with creating all of your Cocoa classes.

Coredata integration into existing application problem iphone

I'm integrating coredata into my existing application as given in http://wiresareobsolete.com/wordpress/2009/12/adding-core-data-existing-iphone-projects/.
I'm facing a problem in insertion of data. My data isn't getting inserted into the following entity.
I'm importing coredata framework into the class Editorial.
#interface Editorial : NSManagedObject {
NSInteger id;
NSString *type;
}
#property (nonatomic, assign) NSInteger id;
#property (nonatomic, retain) NSString *type;
And in Editorial.m I'm writing:
#implementation Editorial
#synthesize id, type;
In my .xcmodel also, Editorial is subclassing NSManagedObject and having the mentioned variables with corresponding types.
I think I'm missing something very obvious. But I'm not getting it. Generally while using coredata, if created at the beginning of the project, it automatically inserts attributes and they are not declared in interface and are synthesized with #dynamic. But while integrating coredata at later time, should the corresponding classes be created the way coredata creates them for us?
EDIT: This is how I'm inserting values for Editorial object.
self.managedObjectContext = appDelegate.managedObjectContext;
newEditorial = (Editorial *)[NSEntityDescription
insertNewObjectForEntityForName:#"Editorial"
inManagedObjectContext:self.managedObjectContext];
strTitle = [NSString stringWithFormat:#"%#",[object valueForKey:#"eletitle"]];
[newEditorial setEletitle:[NSString stringWithFormat:#"%#", strTitle]];
[newEditorial setElecompany:[NSString stringWithFormat:#"%#", strTitle]]; // CRASHING HERE
[self saveAction];
One more thing that it's crashing at the 2nd string insertion at shown line. I'm getting
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '* -[NSManagedObject setElecompany:]: unrecognized selector sent to instance 0x4658800' at this line.
NSString *eleCompany exists in the specified coredata entity as well as the class. Also strTitle is containing string and not assigning it to eleTitle as well as eleCompany both of which are strings and exist in class as well as in coredata entity.
Can anybody please help?
This' really urgent.
Thanx in advance.
NSManagedObject instances probably do not contain NSInteger attributes — you probably meant to use NSNumber instead, which is a Core Foundation object type that can be serialized in Core Data.
id is a keyword and very likely reserved. Try naming your NSNumber attribute differently.
After updating your model with new attribute and relationship changes, you will always need to modify or recreate header and implementation files for your managed objects.

insertNewObjectForEntityForName:inManagedObjectContext: returning NSNumber bug?

I'm relatively well versed in CoreData and have been using it for several years with little or no difficulty. For the life of me, I can't figure out why
insertNewObjectForEntityForName:inManagedObjectContext:
is all of a sudden returning some sort of strange instance of NSNumber. GDB says the returned object is of the correct custom subclass of NSManagedObject, but when I go to print a description of the NSManagedObject itself, I get the following error:
*** -[NSCFNumber objectID]: unrecognized selector sent to instance 0x3f26f50
What's even stranger, is that I'm able to set some relationships and attributes using setValue:forKey: and all is good. But when I try to set one specific relationship, I get this error:
*** -[NSCFNumber entity]: unrecognized selector sent to instance 0x3f26f50
I've tried everything from clean all targets, to restarting both mac and iPhone, even editing the model so that the relationship in question is to-one instead of to-many. No matter what I do, the same problem appears. Has anyone ever seen anything like this before?
I had the very same issue: I had added a method called "isDatabase" (returning a BOOL) to the parent entity of my Database entity, which had a relationship named "database". Renaming "isDatabase" to "isOfTypeDatabase" fixed the issue. So keep also looking in parent entities!
I defined a property on an NSManagedObject subclass that collided with the name of a relationship defined on the class.
Here's the code in my MyManagedObjectSubclass+Custom.h
#property (readonly, nonatomic) BOOL isSeason;
Here's the code produced by XCode for MyManagedObjectSubclass.h
#property (nonatomic, retain) SomeOtherEntityToOneRelationship *season;
Note that isSeason, by KVC, will collide with the season name
I ran into the exact same problem and after pulling my hair out for an entire day, I solved my problem.
I believe the problem is related to a corrupt attribute / relationship, and the NSCFNumber is actually looking for the objectID for that attribute / relationship. In my case, I could use valueForKey: to find all of the attributes / relationships, although a relationship I had called "file" seemed to be corrupt.
I finally realized that I had extended NSObject to include a boolean "isFile" method, and somehow this was interfering with CoreData and causing it to either return a corrupt object, or not be able to deal properly with the object it had. My guess is that CoreData must dynamically create "isXXX" methods.
I could either fix the problem by removing the isFile method, or by renaming my property.
The objectID and entity selectors are on NSManagedObject, not NSCFNumber (or NSNumber). I wouldn't expect you to call either of these selectors on a NSNumber which should be a property on an entity, not the entity itself.
Every entity in CoreData must extend NSManagedObject, so your NSCFNumber object is not an entity.

Why does Core Data create NSManagedObject's with properties but no accompanying instance variable?

Exactly as the title says, why does Core Data create instances of NSManagedObject with properties for each entity's attribute, but no accompanying instance variable? The problem is, I would like to use some of my 'Entities' in a typical alloc/init style fashion in some parts of my code. Not using Core Data's fetching/context to create/store.
I suppose I could stay without the instance variables, but would it hurt to add them? Can I also change the #dynamic to #synthesize so I can use KVC on my properties/ivars?
Perhaps I'm completely wrong in trying to use one of my Core Data entities in some parts of my code without using the core data APIs. Still rather new to it to understand when I can go my own route.
The reason it doesn't use ivars is that that data isn't there. When you load an object it may not fault in all of its data. ivars are just variables, if you and write to them NSManagedObject doesn't have a chance to fault in the value if it has not been loaded yet. Via accessors NSManagedObject has a choke point that allows it to read the data off the disk if it has not been faulted in yet, which means your object graph can be brought in lazily. Otherwise you would need to bring in every connected object.
Because of that, you can't just add ivars, they won't have the values you want. Likewise you can't change from #dynamic to #synthesized, things will not behave correctly. The dynamic property implementations provided by CoreData are completely KVC and KVO compliant anyway.
If you want to access the values without tripping KVO or KVC you can access the "primitive" values. You do that in one of two ways. You use primitiveValueForKey: and setPrimitive:valueForKey: or you can just declare the primitive and let CD provide dynamic implementations (from the documentation)
#interface Department : NSManagedObject
{
}
#property(nonatomic, retain) NSString *name;
#end
#interface Department (PrimitiveAccessors)
- (NSString *)primitiveName;
- (void)setPrimitiveName:(NSString *)newName;
#end
You don't need instance variables with Core Data. A managed object stores these values elsewhere.
I've never gotten very far with Core Data, but if I remember correctly, you're supposed to use primitiveValueForKey: and setPrimitiveValue:forKey: to perform these accesses from your accessor methods. (Outside of your accessors, you should be using either the accessors themselves or valueForKey:/setValue:forKey: instead.)