insertNewObjectForEntityForName:inManagedObjectContext: returning NSNumber bug? - iphone

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.

Related

CoreDataGeneratedAccessor method giving "unrecognized selector sent to instance" error

When i call the method:
- (void)removeObjectFromMediaAtIndex:(NSUInteger)idx;
which is one of the default methods in a file created as a core data object, i'm getting an error of unrecognized selector sent to instance. Anybody know why this might be happening?
Ensure that your NSManagedObject sublcass instance was created using an NSManagedObjectContext and not directly. Instead of leveraging #synthesize for properties, NSManagedObject sublcasses leverage the #dynamic keyword which indicates the accessors will be created at runtime - in this case, by the NSManagedObjectContext. They will not be there if you create the object instance using something like alloc]init];
It is a notorious Core Data bug. It is almost 2-year old but sadly it is still there. See this post: Exception thrown in NSOrderedSet generated accessors.
It sounds like you may have altered your data model without altering the classes, or vice-versa. Or perhaps one of your team members did (my team quickly learned about this danger). Another possibility is that the reference you are using is not actually the class you think it is. Sometimes if you overrelease an object, another object will occupy the previous memory space but it will not be the correct class.
However, this doesn't look like a default method. The default methods I am used to seeing are add object, remove object, change to a new NSSet, and one more that I can't quite remember off the top of my head. However, if you got the CoreData object to use an NSArray instead it would make sense.

iPhone Core Data - cannot fulfill a fault error

I have three classes, A, B and C. A is the main class.
When the user wants to see the list of all objects that were purchased, Class B is called from A and shows the list of objects in a core data entity.
Inside class B, the user can buy new objects (in-app purchase). When the user wants to buy another object, class C is called.
When class C is called, a new object is created on the core data entity using
anObject = [NSEntityDescription insertNewObjectForEntityForName:#"Objects" inManagedObjectContext:context];
this object is then assigned to a local reference on Class C, using something like
self.object = anObject;
this object variable was declared like this:
.h
MyObjects *object;
#property (nonatomic, retain) MyObjects *object;
and #synthesized on .m
MyObjects is a core data class representing the entity.
In theory, object will retain anything assigned to it, so the line self.object = anObject I typed previously will retain anObject reference on self.object, right?
The problem is that when I try to access self.object in the same class after buying the new object, I receive an error "CoreData could not fulfill a fault for XXX", where XXX is exactly self.object.
At no point in the code there's any object removal from the database. The only operation to the database I could identify was a saving operation done by another class moments before the crash. The save is done by something like
if (![self.managedObjectContext save:&error]) ...
Is there any relation? what may be causing that?
CoreData manages the lifetime of managed objects and you should not retain and release them. If you want to keep a reference to the object so that it can be retrieved later then you have to store the object's id (obtained using -[NSManagedObject objectID]). Then use that to retrieve the object later using -[NSManagedObjectContext objectWithID:].
Make sure you understand about CoreData faulting. Read the documentation.
I had a similar issue a few days ago (using NSFetchedResultsController) where I was placing my fetchedObjects into an array and gathering attributes to populate tables from the array objects. It seems that if the objects in the array are faulted, you cannot unfault it unless you are acting on the direct object. In my case, I solved the issue by taking the lines of code in question and calling [[_fetchedResultsController objectAtIndexPath:indexPath] someAttribute]. I would assume that doing something similar would fix your problem as well. It seems a bit tedious to need to fetch from the managedObjectContext to obtain a faulted value, but this was the only way I could personally get past the issue.
Core Data is responsible for managing the lifetime of managed objects in memory. It's really important to understand Managed Object Contexts - Read the documentation.
Apple also provides an entire troubleshooting section here, and it contains among other things the causes for your error. But it's really only useful if you understand how core data works.
Most likely error is that the object you are saving does not belong to the managed object context.
Say you use the same object on different threads and those different threads use different managed object context, then this will happen.

does anyone have a working example of a fetched-property in core-data?

I have tried to use fetched properties a couple of times, and although it seems to be the right approach, it never works.
In my latest attempt I added the fetched-property to my entity, selected the other entity in the model as the 'destination', and set the predicate to a condition that I know is valid.
Problem 1: When the NSManagedObject-class for the entity is generated it does not include anything for the fetched-property. After some searching I added the declaration for it in the .h file and the #dynamic statement for it in the .m file (yes, I know it's an NSArray * type).
Problem 2: Even after that, when I access this property in code I get an exception being thrown that states something to the effect that the fetch-request does not have an entity. I am assuming that the 'entity' would be the one specified as the 'destination' and it is, in fact, there.
So, I'd like someone to provide a concrete working example (iOS platform) where a fetched-property is defined in the model, declared in a NSManagedObject-derived class, and actually used from code.
At this point I am giving up on this time-waster and simply implementing the fetch-request code myself.
Here's my relevant bits of code (including bits you've already mentioned):
My example has a 'Card' object that has a 1->many relationship with a 'Stats' object. Each 'Stats' object has an 'outcome' that can be 1-4. My fetched property is a simple one to give my 'Card' object an array of 'Stats' objects that are of 'outcome'=1 only.
I wanted to use the fetched property so that I could easily get hold of 'Card' objects that had more than a certain number and kind of 'Stats' objects.
So, in the 'Card' object I put the Fetched Property 'statsOfTypeOne', with Destination set to 'Stats'.
In the predicate for this fetched property I put
(SELF.outcome=1) AND (SELF.card=$FETCH_SOURCE)
'SELF' is the 'stats' record, and $FETCH_SOURCE magically becomes the 'Card' object when executed.
As you did, I put the following in the .h and .m files for the 'Card' object:
#property (nonatomic, retain) NSArray *statsOfTypeOne;
#dynamic statsOfTypeOne;
Then in my code I used:
[self.managedObjectContext refreshObject:cardInstance mergeChanges:YES];
[cardInstance valueForKey:#"statsOfTypeOne"]
to get at the array (although cardInstance.statsOfTypeOne should be fine). Without the refresh object it wasn't updating the Fetched property (as per the manual).
I think that's everything that I did to make it work. Let me know if it works for you.
Peter
Adding to #Peter's answer. Here's how I got it working in Swift 2.0 and Xcode 7:
import Foundation
import CoreData
#objc(Card)
class Card: NSManagedObject {
#NSManaged var statsOfTypeOne: [Stat]
}
And then, to read the fetched property:
managedObjectContext.refreshObject(someCard, mergeChanges: true)
// This works and returns [Stat] type
someCard.statsOfTypeOne
// So does this
someCard.valueForkey("statsOfTypeOne") as! [Stat]
Have you taken a look at this previous question: Xcode 4 Core Data: How to use fetched property created in Data Model editor
Read through the accepted answer and all of the comments. It sounds like they have it sorted out.

Why do some of my core data objects have managedObjectContext set to nil?

I was having problems adding objects to one of my core data objects that has a relationship and after doing some investigating I realized it was because that objects managedObjectContext was nil (0x0). Some more investigating revealed that anytime I created an object of that type, the managedObjectContext was always nil. Every object I create of any other type has the correct managedObjectContext. And no, the object was not deleted, this is immediately after calling NSEntityDescription:insertNewObjectForEntityForName:inManagedObjectContext
Why would only this particular object be broken? It's also strange that even though the managedObjectContext seems to be nil, it otherwise works correctly (other than the relationship problem). I've tried resetting the simulator several times and I've even regenerated the .h and .m files that core data produces for that object but I can't get it to work. As far as I can tell, there are no errors, the managedObjectContext just never gets set.
I figured it out. I had added a member variable called managedObjectContext, which I instinctively add to most classes in my application since I pretty much always need it. What I didn't realize is that I was overriding the methods of the same name in the parent class NSManagedObject. That's why it was reset to be nil and still worked until I tried to add relationship which queried the managedObjectContext, found it to be nil, and complained.

Implementing Transient Properties

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.