I have a CoreData model in my iPhone app, which is linked to a SQL Database with more than 50k records. When I generate the records classes, Xcode uses the #dynamic directive for properties. I have a property named "ISFAV", NSNumber type (CoreData does not use BOOL or Integer, it uses object types). Being short, I change the ISFAV property when the user taps on a button in this way:
if (![record.ISFAV intValue])
record.ISFAV=[NSNumber numberWithInt:1];
else
record.ISFAV=[NSNumber numberWithInt:0];
Quite simple. But if I try to tap many times on the same button sequentially, the iPhone takes too much time (the button remains in the hold state for a time that increase progressively). This happens even if I change record, adding\removing many records from favorites sequentially (instead of adding\deleting the same record from favorites).
If I change the original accessor method to #synthesize, the problem seems to be solved.
Is it correct to use the synthesize directive for accessor methods in CoreData?
Thank you very much!
#edit
Using the synthesize directive, no changes are made to the CoreData model when I save the context :-\ The problem is still unsolved :-\
#dynamic is a flag that just tells the compiler that the method will exist at run time and to not warn about it now. You should not be using #synthesize with Core Data properties.
How do you know your hotspot is with setting the Core Data property? Have you profiled the code? In my experience, changing one attribute in Core Data is not going to be slow, it will be 1/1000th of a second or faster. Are you saving to disk each time you change that one property? Are you doing something else in the call?
I would profile the code first and find out where the hotspot really is. Use Instruments and confirm.
Related
I'm using an xcdatamodel to define a number of classes based upon CoreData data entities. This is working great and I can retrieve them in accordance to Apple's examples:
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreData/Articles/cdFetching.html
What I'm after however, is a way to package the fetch method up into another class, but I have a couple of questions...
e.g.
MyDataAccessClass *mdac = [[MyDataAccessClass alloc] init];
myFetchedData = [mdac fetchData];
Q1. If I do this, is it ok that the NSManagedObjectContext is defined in the class? or does it still need to be referenced in my view controller and passed to my 'MyDataAccessClass'?
Q2. It makes sense to me to have the data retrieval methods set up in the classes created by XCode for the entities in the data model. Although every time I try to do this, then update those classes automatically because they are automatically generated by XCode, they overwrite any methods I've defined.
Thanks in advance!
You might be able to create a new NSManagedObjectContext in the seperate class - not sure if there will be any issues with that since one is already created in the appDelegate. What I did was what you suggest in the second part of Q1, where I pass the NSManageObjectContext into the seperate method class so then I can do something like:
myFetchedData = [mdac fetchData:currentNSManagedObjectContext];
Hi
Normally all the methods like
'- (NSFetchedResultsController *)fetchedResultsController '
are placed in the code of view controllers. I find it a bit messy to write Data Fetching code along with lifecycle methods or table delegate methods.
So my point is should I refactor the CoreData methods to some other helper class say DataLoader and then call them in view controllers?
Is this a wrong thing to do or am I going to loose some coding benefits of Core Data methods.
I would say moving the fetchedResultsController to a helper class is a good idea.
A problem I encounter often is to get the spelling of attributes right.
For example I do a predicate and want to filter on an attribute called #"isSelected". There is no check by the compiler nor by the linker to check the string isSelected. I will have to double check each line where the string has been used.
A search&replace won't work on the misspellings because I don't know what bugs have been introduced.
When I get the predicate wrong then no results will be fetched. Problem is that I don't know if there are no matching rows or if I have filtered wrong. I will need to check at runtime and that consumes time.
For predicates the saved templates exist, so predicates are not a perfect example. But think about value forKey: and we are at square one.
Now if all the fetchedResultsController are in one file then checking would become easier. At least it reduces the possibility of missing that little misspelling in a far away and rarely used class.
...or am I going to loose some coding benefits of Core Data methods.
I tend to say no, but other please feel free to jump in.
#Mann yes of course you can do that without loosing any coding benefits.....if u don't want to write Data Fetching code in you view Controller do not write there.....Make any other class lets say it DataLoader and write the fetching code in method of this class......and call this method by making the object of DataLoader class ....u will be able to fetch data from database
Hope u get it!
I've created a program that uses core data and it works beautifully.
I've since attempted to move all my core data methods calls and fetch routines into a class that is self contained. My main program then instantiates that class and makes some basic method calls into that class, and the class then does all the core data stuff behind the scenes. What I'm running into, is that sometimes I'll find that when I grab a managed object from the context, I'll have a valid object, but its properties have been deallocated, and I'll cause a crash. I've played with the zombies and looked for memory leaks, and what I have gathered is it seems that the run loop is probably responsible for deallocating the memory, but I'm not sure.
Is there a way to determine if that memory has been deallocated and force the core data to get it back if I need to access it? My managedObjectContext never gets deallocated, and the fetchedResultsController never does, either.
I thought maybe I needed to use the [managedObjectContext refreshObject:mergeData:] method, or the [managedObjectContext setRetainsRegisteredObjects:] method. Although, I'm under the impression that last one may not be the best bet since it will be more memory intensive (from what I understand).
These errors only popped up when I moved the core data calls into another class file, and they are random when they show up.
Any insight would be appreciated.
-Ryan
Sounds to me like you are not retaining objects you want to keep hanging around. If you are doing something like this:
NSArray *array = [moc executeFetchRequest:request error:&error];
you do not own the returned array and it will most likely disappear when the current autorelease pool is drained. This will occur when the run loop finishes processing the current event.
All this is speculation. If you want a proper answer, you need to post your code.
It's hard to know what the problem is based on your description, but you might want to look at the Core Data memory management guide. You shouldn't have to worry about memory management for managed objects and their entities (they're fetched and faulted automatically). When you talk about "properties," do you mean custom properties backed by ivars? If so, these should be released in didTurnIntoFault and allocd as needed (probably in the accessor).
I was struggling with a similar issue. I'm using a managed object class and want to set its properties dependent on user input. But the sometimes the properties and sometimes the whole managed object were deallocated.
After reading the Apple documentation http://developer.apple.com/library/IOs/#documentation/Cocoa/Conceptual/CoreData/Articles/cdMemory.html the chapter "The Role of the Managed Object Context" I learned that managed objects are released each run loop completes.
And there is the golden advice to set
[myMangedObjectContext setRetainsRegisteredObjects:YES];
(I had to set it in the init method (initWithNibName for me) of my view controller.)
You should also regard to retain only the objects you need to as explained in the documentation. But read it yourself.
If I'm not right please correct me.
I also made a class that handles all my CoreData fetching and stuff. I ran into a couple of gotcha's, so here are some tips. (If I am making any memory management errors in these examples, please let me know.)
Two things:
1) Made a "fetchFiredObject" method in the CoreData handler class. So when I want to get a managedObject that has all its variables and is a "fully feathered bird" so to speak, instead of doing:
aManagedObject *myManagedObject = [myCoreDataHandler.managedObjectStorageArray objectAtIndex:1];
int x = myManagedObject.someVariable.intValue;
instead I do:
aManagedObject *myManagedObject = [myCoreDataHandler fetchFiredObjectAtIndex:1];
int x = myManagedObject.someVariable.intValue;
And in myCoreDataHandler's fetchFiredObjectAtIndex:i method, we're going into the array, finding the object key at index i, then doing a fetchRequest for that object key, and returning the freshly-fetched managedObject so that it won't have been faulted or deallocated, etc. :D
2) When I create a new child viewController, I populate its "myCoreDataHandler" value from the parent upon creation. However, this happens on a subsequent line of code after the line of code that creates the new viewController. Therefore, any code in the child's viewDidLoad that tries to use myCoreDataHandler's methods will return empty objects because viewDidLoad completes before the parent's next line of code where it sets the values of globals in the child object. So make sure you are not accessing your "Core Data handling object" from within viewDidLoad or anything local methods called by viewDidLoad! Instead call them from the parent after creating the new viewController.
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.)
I'm trying to use ABRecordRef within an NSMutableArray, but it doesn't seem to work. I know that ABRecord is a C class, but I thought that ABRecordRef was the work around Objective-C class that allowed me to use it with NSObjects. What do I need to do to make this work?
What do you mean by "Not Working"? As in, you get compile or run-time errors?
As I noted in the response to the other poster, you can't use the Objective-C API on the iPhone (There also is no true ABrecord class to brdge to).
Generally it's a really good idea with the address book stuff on the iPhone to copy out elements you are interested in, and save the copied values off in something like a dictionary. If you need to save all the elements, you have to have code that reads every value as defined in the AddressBook.h header file, there's no API way to generically walk the records.
Also remember that at any time, the user might change the address book if they quit your app and come back - so be careful about what you change after they relaunch the app if you are storing values!!