Create NSManagedObject (big size). Memory warning and the application crashes - objective-c

I'm a new iOS developer. Hope for help.
I want to be able to create many NSManagedObjects. The size of fields of one NSManagedObject is about 5Mb. I couldn't save such a big amount of memory in the iPhone memory. And I want to save it in the database. But when I save the NSManagedObject, it's still in the memory, because when I save about 20 objects, I get the memory warning and the application crashes.
Here is my code
- (void)SaveItem
{
NSString *entityName = kEntityName;
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = appDelegate.managedObjectContext;
NSEntityDescription *entityDesctiption = [NSEntityDescription
entityForName: entityName
inManagedObjectContext:context];
// check if town exists
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"id == %d", self.imageID];
NSFetchRequest *requestToCheckExistense = [[NSFetchRequest alloc] init];
[requestToCheckExistense setEntity:entityDesctiption];
[requestToCheckExistense setPredicate:predicate];
NSArray *objects = [context executeFetchRequest:requestToCheckExistense error:nil];
[requestToCheckExistense release];
if (objects == nil)
{
NSLog(#"there was an error");
}
NSManagedObject *object;
if ([objects count] > 0)
{
// edit item
object = [objects objectAtIndex:0];
}
else
{
// if object doesn't exist, find max id to imlement autoincrement
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDesctiption];
request.propertiesToFetch = [NSArray arrayWithObjects: #"id", nil];
NSArray *allobjects = [context executeFetchRequest:request error:nil];
[request release];
NSInteger newID = 1;
if ([allobjects count] > 0)
{
NSNumber *maxID = [allobjects valueForKeyPath:#"#max.id"];
newID = [maxID intValue] + 1;
}
// write item
object = [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:context];
[object setValue:[NSNumber numberWithInt:newID] forKey:#"id"];
self.imageID = newID;
}
// fill NSManagedObject
// size of objNSData is about 5MB
NSMutableData *objNSData = [[DatabaseManager sharedDatabaseManager] encryptedDataFromImage:bigImage];
[object setValue:objNSData forKey:#"big"];
[context save:nil];
}
When I try to call [self SaveItem] 20 times , the app crashes with memory warning. When I commented
[object setValue:objNSData forKey:#"big"];
everything was OK.
I tried to add the code to #autoreleasepool , but that didn't help.
I know, that now, when I save data to database, it's still in iPhone memory. How to release it from this memory?
When I get a set of Managed Objects, they are not in the memory (I can easyly get 100 object, each of them has 5Mb fields)

Storing images (or big binary values) in core data is not a good option. Apple recommends against it. There are rules for that. Check this other question/answer.
If you want to get the objects out of memory, you need to remove all references you have for them. If you are executing "SaveItem" in a tight loop, you should wrap every loop execution in an autorelease pool, not the loop itself.
From apple docs, that may be your problem too:
Managed objects that have pending changes (insertions, deletions, or updates) are retained by their context until their context is sent a save:, reset , rollback, or dealloc message, or the appropriate number of undos to undo the change.
The undo manager associated with a context retains any changed managed objects. By default, the context's undo manager keeps an unlimited undo/redo stack. To limit your application's memory footprint, you should make sure that you scrub (using removeAllActions) the context's undo stack as and when appropriate. Unless you retain a context's undo manager, it is deallocated with its context.
If you do not intend to use Core Data's undo functionality, you can reduce your application's resource requirements by setting the context’s undo manager to nil. This may be especially beneficial for background worker threads, as well as for large import or batch operations.

Related

CoreData, child MOC on separate thread, unexpected: error: NULL _cd_rawData but the object is not being turned into a fault

Ok, im a bit lost with this one, i am currently trying to run a background core data operation using a second ManagedObjectContext with its type set to NSPrivateQueueConcurrencyType and failing miserably with the above error.
I have a custom subclass of NSOperation, which is being passed an NSArray of strings, and the PersistentStoreCoordinator from the main thread, it then creates its own ManagedObjectContext, runs a query and performs and operation.
Here is the code from the class:
//
// ProcessProfanity.m
// Hashtag Live Desktop
//
// Created by Gareth Jeanne on 24/03/2014.
// Copyright (c) 2014 Gareth Jeanne. All rights reserved.
//
#import "ProcessProfanity.h"
#import "Tweet.h"
static const int ImportBatchSize = 250;
#interface ProcessProfanity ()
#property (nonatomic, copy) NSArray* badWords;
#property (nonatomic, strong) NSManagedObjectContext* backgroundContext;
#property (nonatomic, strong) NSPersistentStoreCoordinator* persistentStoreCoordinator;
#end
#implementation ProcessProfanity
{
}
- (id)initWithStore:(NSPersistentStoreCoordinator*)store badWords:(NSArray*)words
{
self = [super init];
if(self) {
self.persistentStoreCoordinator = store;
self.badWords = words;
}
return self;
}
- (void)main
{
_backgroundContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
_backgroundContext.persistentStoreCoordinator = [self persistentStoreCoordinator];
_backgroundContext.undoManager = nil;
[_backgroundContext performBlockAndWait:^
{
[self import];
}];
}
- (void)import
{
//Create new fetch request
NSFetchRequest *request = [[NSFetchRequest alloc] init];
//Setup the Request
[request setEntity:[NSEntityDescription entityForName:#"Tweet" inManagedObjectContext:self.backgroundContext]];
NSError *error = nil;
//Create an array from the returned objects
NSArray* tweetsToProcess = [self.backgroundContext executeFetchRequest:request error:&error];
NSAssert2(tweetsToProcess != nil && error == nil, #"Error fetching events: %#\n%#", [error localizedDescription], [error userInfo]);
for (Tweet* tweetToCheck in tweetsToProcess){
__block NSString *result = nil;
[self.badWords indexOfObjectWithOptions:NSEnumerationConcurrent
passingTest:^(NSString *obj, NSUInteger idx, BOOL *stop)
{
if (tweetToCheck){
if ([tweetToCheck.text rangeOfString:obj].location != NSNotFound)
{
result = obj;
*stop = YES;
//return YES;
}
}
return NO;
}];
if (!result){
//DDLogVerbose(#"The post does not contain any of the words from the naughty list");
if(tweetToCheck){
tweetToCheck.profanity = [NSNumber numberWithBool:false];
}
}
else{
if(tweetToCheck){
//DDLogVerbose(#"The string contains '%#' from the the naughty list", result);
tweetToCheck.profanity = [NSNumber numberWithBool:true];
}
}
}
[self.backgroundContext save:NULL];
}
#end
And this is how i am calling it:
-(void)checkForProfanity{
if(!self.operationQueue){
self.operationQueue = [[NSOperationQueue alloc] init];
}
NSArray* termsToPass = [self.filterTerms copy];
ProcessProfanity* operation = [[ProcessProfanity alloc] initWithStore:self.persistentStoreCoordinator badWords:termsToPass];
[self.operationQueue addOperation:operation];
}
Edit 1
The specific line i seem to be getting the error on, or at least where Xcode is breaking is:
if ([tweetToCheck.text rangeOfString:obj].location != NSNotFound)
I have managed to narrow this down a bit, the NSArray that contains the list of terms to search the strings for is potentially quite large, possibly over a 1,000 NSStrings. If i test with an array of that size, i get the issue. However if i reduce the array to around 15 NSStrings, i do not get the error, so i don't think this is necessarily a thread related issue, i'm wondering if the array is getting released in the main thread. I have modified the code to make a deep copy, and then a __block copy as follows, but it doesn't seem to have helped.
self.badWords = [[NSArray alloc] initWithArray:words copyItems:YES];
and
for (Tweet* tweetToCheck in tweetsToProcess){
__block NSArray *array = [[NSArray alloc] initWithArray:self.badWords copyItems:YES];
__block NSString *result = nil;
[array indexOfObjectWithOptions:NSEnumerationConcurrent
In fact, at the point where Xcode breaks, if i PO array, i get an object not found message, but if i po result, i correct get an object returned that is nil.
Edit 2
So i have made the following changes, with no change:
Made the NSArray strong rather than copy:
#property (nonatomic, strong) NSArray* badWords;
And made it a copy when allocated:
self.badWords = [[NSArray alloc] initWithArray:words copyItems:YES];
And created a local copy of the NSArray with the ___block declaration inside the actual method processing the objects:
__block NSArray *array = [[NSArray alloc] initWithArray:self.badWords copyItems:YES];
Which should surely mean it sticks around for the life of the ProcessProfanity object?
Am i wrong in expecting to be able to PO the array from the breakpoint within the block?
In this instance the error message "error: NULL _cd_rawData but the object is not being turned into a fault" indicates that you are accessing a managed object outside of its context. Basically your fetch returns all the Tweets from your persistent store as faults. Once you try and access a property on the Managed Object, Core Data will fire a fault and fetch the full object from the store.
By calling the NSArray method indexOfObjectWithOptions:passingTest: with an option of NSEnumerationConcurrent you are implying that you want to perform asynchronous execution on the elements in your array. The keyword concurrent indicates that multiple threads can be used to operate on the array elements.
In your context this means that accessing a managed object inside this block might result in accessing it on a different thread from the managed object context that owns the object. So when you access tweetToCheck.text in your conditional check - if ([tweetToCheck.text rangeOfString:obj].location != NSNotFound), under the hood Core Data is fetching that managed object from the persistent store and returning it to a thread that is not part of the managed object contexts thread.
Furthermore, it is not necessary to use the method indexOfObjectWithOptions:passingTest: since you are not actually interested in the result of this operation.
It seems to me that it might be more convenient for you to use an NSSet as you are only testing to see whether or not a given tweet word exists in your profane words. Quoting the documentation for NSSet: "You can use sets as an alternative to arrays when the order of elements isn’t important and performance in testing whether an object is contained in the set is a consideration". Clearly this seems to meet your criteria.
So your init would look like:
-(id)initWithStore:(NSPersistentStoreCoordinator*)store
badWords:(NSSet*)badWords
{
self = [super init];
if(self) {
self.persistentStoreCoordinator = store;
self.badWords = [words copy];
}
return self;
}
Since you are only interested in updating tweets that have not yet been tagged for profanity you would probably only want to fetch tweets that haven't been flagged profane:
//Create new fetch request
NSFetchRequest *request = [[NSFetchRequest alloc] init];
//Setup the Request
[request setEntity:[NSEntityDescription entityForName:#"Tweet" inManagedObjectContext:self.backgroundContext]];
[request setPredicate:[NSPredicate predicateWithFormat:#"profanity = NO"]];
Now that you have an array of tweets that are not profane you could iterate through your tweets and check each word if it contains a profane word. The only thing you will need to deal with is how to separate your tweet into words (ignoring commas and exclamation marks etc). Then for each word you are going to need to strip it of diacritics and probably ignore the case. So you would end up with someone along the lines of:
if([self.badWords containsObject:badWordString]) {
currentTweet.profanity = [NSNumber numberWithBOOL:YES];
}
Remember, you can run predicates on an NSSet so you could actually perform a case and diacritic insensitive query:
NSPredicate *searchPredicate = [NSPredicate predicateWithFormat:#"SELF = %#[cd]",wordToCheck];
BOOL foundABadWord = ([[[self.badWords filteredSetUsingPredicate:searchPredicate] allObjects] count] > 0);
Another thing you might want to consider is removing duplicate words in your tweets, you don't really want to perform the same check multiple times. So depending on how you find the performance you could place each word of your tweet into an NSSet and simply run the query on the unique words in your tweet:
if([[self.badWords intersectsSet:tweetDividedIntoWordsSet]) {
//we have a profane tweet here!
}
Which implementation you choose is up to you but assuming you are only using english in your app you are definitely going to want to run a case and diacritic insensitive search.
EDIT
One final thing to note is that no matter how much you try, people will always be the best means of detecting profane or abusive language. I encourage you to read this SO's post on detecting profanity - How do you implement a good profanity filter?
Ok, so still not quite sure what was going on, but i followed Daniels advice and re-wrote the indexOfObjectWithOptions method and now it's working. For completeness, and so it hopefully helps someone else, this is what i ended up doing.
DDLogInfo(#"Processing posts to check for bad language");
for (Tweet* tweetToCheck in tweetsToProcess){
__block NSArray *array = [[NSArray alloc] initWithArray:self.badWords copyItems:YES];
__block NSString *result = nil;
NSRange tmprange;
for(NSString *string in array) {
tmprange = [tweetToCheck.text rangeOfString:[NSString stringWithFormat:#" %# ", string]];
if (tmprange.location != NSNotFound) {
result = string;
DDLogVerbose(#"Naughty Word Found: %#", string);
break;
}
}
if (!result){
//DDLogVerbose(#"The post does not contain any of the words from the naughty list");
if(tweetToCheck){
tweetToCheck.profanity = [NSNumber numberWithBool:false];
}
}
else{
if(tweetToCheck){
//DDLogVerbose(#"The string contains '%#' from the the naughty list", result);
tweetToCheck.profanity = [NSNumber numberWithBool:true];
}
}

Core Data NSTableView do not match

I'm having an issue with Core Data. I have an Array Controller setup that links the data to an NSTableView.
I have a function that runs code every few seconds and in it I display the list of files
NSEntityDescription *entity = [NSEntityDescription entityForName:#"File"
inManagedObjectContext:[self managedObjectContext]];
[request setEntity:entity];
NSError *error = nil;
NSArray *fetchedObjects =[_managedObjectContext executeFetchRequest:request
error:&error];
for (NSManagedObject *objectt in fetchedObjects) {
NSLog(#"Object Name: %#", [objectt valueForKey:#"filename"]);
}
NSManagedObject *object = [fetchedObjects objectAtIndex:index];
NSLog(#"Filename: %#, Index: %ld", [object valueForKey:#"filename"], index);
NSString *imageName = [object valueForKey:#"imageName"];
NSImage *image = [object valueForKey:#"taggedImage"];
First time this code is ran the list matches the table columns. When it's ran again what's fetched from the array controller has changed, but not the tableview. So it's not in sync anymore.
The values at the same index for the table and array are different.
Have you manually checked your NSArrayController to see if it is in sync? That would narrow the question down to is the NSArrayController misconfigured or is the NSTableView misconfigured. Your statement claims you are looking at the NSArrayController but when you use the word fetched I worry you are confusing the objects.
Also, is the data in a saved state or still in an unsaved state? That question also impacts display from the NSArrayController and NSTableView.

Method returning object with executeFetchRequest

I searched for the past couple hours reading dozens of posts on the topic of memory management in objective-C and I just don't get it. Sorry. I am doing my best!
What I am looking for at the moment is the answer to how to return an object from a method which calls 'executeFetchRequest'.
Here is some code...
+ (Player *)loadPlayerWithPredicate:(NSString *)name:(NSInteger)index
{
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Player" inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
// Set filter predicate
NSString *strFilter = [NSString stringWithFormat:#"%# = %d", name, index];
[request setPredicate:[NSPredicate predicateWithFormat:strFilter]];
// Create the sort descriptors array
NSSortDescriptor *sorter = [NSSortDescriptor sortDescriptorWithKey:name ascending:YES];
[request setSortDescriptors:[NSArray arrayWithObject:sorter]];
NSError *error = nil;
Player *player = nil;
NSArray *array = [managedObjectContext executeFetchRequest:request error:&error];
if ([array count] == 1)
{
player = [array objectAtIndex:0];
//[player retain]; // ???
}
[request release];
return player;
}
But what I really need is for the "player" object to stick around after the method returns.
How can I make 'player' live after being returned?
Should I call 'retain'? (and then I would call 'release' in the receiver)
Should I 'copy' the object?
I read all of the other posts about alloc, copy, new, etc.
I just need a simple example so that I can then derive understanding from it. If you can also show me an example of the receiver calling this method I would appreciate it.
Thanks!
You could use copy, but it would require that the Player class conforms to the NSCopying protocol, which I doubt it does.
The simplest (and probably best) way to do this here is as such:
if ([array count] == 1)
{
player = [[array objectAtIndex:0] retain];
}
[request release];
return [player autorelease];
You are keeping the player from being released for the meantime by retaining it, and then when you return it you use autorelease. This is not strictly necessary, but I think it is good programming practice in this case. This is because your
+ (Player *)loadPlayerWithPredicate:(NSString *)name:(NSInteger)index;
function name would imply (in Obj-C standard practice) that the object returned is autoreleased, thus leaving the memory management of the object up to the caller.
In the class that you call + (Player *)loadPlayerWithPredicate:(NSString *)name:(NSInteger)index, you will need to determine if you want to retain the returned Player (such as setting it to a retain property), or if you want to leave it as is (autoreleased, and thus will likely be released after the method this call is in has finished. Use this if you only need to perform a few actions with it immediately and don't need to hold on to it after).

NSTokenFieldCell Subclass to force use of Core Data To-Many Relationship

I have come across an interesting conundrum (of course, I could just being doing something horribly wrong).
I would like an NSTokenField to "represent" a relationship in a Core Data Application. The premise is such: You click on a Note from a TableView (loaded from the Notes Array Controller). The token field is then bound (through "value") to the Notes Array Controller selection.Tags. Tags is a to-many relationship on the entity Notes.
Obviously, an NSTokenField will not accept the NSSet that the Array Controller Provides it. To get around this, I subclassed NSTokenFieldCell and overrode its objectValue and setObjectValue: methods. I thought that I could simply translate the NSSet that was being provided to the NSArray that the NSTokenFieldCell expected. (Note: I originally tried overriding these methods on a NSTokenField subclass; however, they were not being called.)
So, I came up with said code:
- (void)setObjectValue:(NSSet*)object {
tagsList = [object copy];
NSMutableArray *displayList = [[NSMutableArray alloc] init];
for (id newObject in tagsList) {
[displayList addObject:[newObject valueForKey:#"Name"]];
}
[super setObjectValue:displayList];
}
- (id)objectValue {
NSArray *displayList = [super objectValue];
NSEntityDescription *tagEntity = [NSEntityDescription
entityForName:#"Tag"
inManagedObjectContext:[appDelegate
managedObjectContext]];
NSMutableSet *returnValue = [[NSMutableSet alloc] init];
for (NSString *token in displayList) {
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:tagEntity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"Name == %#", token];
[request setPredicate:predicate];
NSError *error;
NSArray *results = [[appDelegate managedObjectContext] executeFetchRequest:request error:&error];
if (results == nil) {
NSManagedObject *object = [NSEntityDescription insertNewObjectForEntityForName:#"Tag" inManagedObjectContext:[appDelegate managedObjectContext]];
[object setValue:token forKey:#"Name"];
[returnValue addObject:object];
} else {
[returnValue addObject:[results objectAtIndex:0]];
}
}
return returnValue;
}
It crashes. :( And, surprisingly it crashes on the line that calls [super objectValue]. It gives me the error:
-[NSConcreteAttributedString countByEnumeratingWithState:objects:count:]: unrecognized selector sent to instance ...
Sigh. The sad thing is that when I go into the Core Data XML file and give the Note a Tag, it displays correctly, and [super setObjectValue:] is passed an array of strings. However, as soon as I enter something else and mouse away, I get the error.
I am not sure what to do about this. Can anyone spot anything horribly wrong with this? Thanks.
UPDATE:
If it makes a difference, I do not have a delegate configured for the TokenField.
In typical SO fashion, I found the answer to my own question. It was silly to begin with. I simply needed another ArrayController bound to the Notes selection.Tags set. Then, I bound the NSTokenField to the ArrangedObjects of that Controller, implemented some delegate methods. Boom. Simple.
Silly me.

Adding unique objects to Core Data

I'm working on an iPhone app that gets a number of objects from a database. I'd like to store these using Core Data, but I'm having problems with my relationships.
A Detail contains any number of POIs (points of interest). When I fetch a set of POI's from the server, they contain a detail ID. In order to associate the POI with the Detail (by ID), my process is as follows:
Query the ManagedObjectContext for the detailID.
If that detail exists, add the poi to it.
If it doesn't, create the detail (it has other properties that will be populated lazily).
The problem with this is performance. Performing constant queries to Core Data is slow, to the point where adding a list of 150 POI's takes a minute thanks to the multiple relationships involved.
In my old model, before Core Data (various NSDictionary cache objects) this process was super fast (look up a key in a dictionary, then create it if it doesn't exist)
I have more relationships than just this one, but pretty much every one has to do this check (some are many to many, and they have a real problem).
Does anyone have any suggestions for how I can help this? I could perform fewer queries (by searching for a number of different ID's), but I'm not sure how much this will help.
Some code:
POI *poi = [NSEntityDescription
insertNewObjectForEntityForName:#"POI"
inManagedObjectContext:[(AppDelegate*)[UIApplication sharedApplication].delegate managedObjectContext]];
poi.POIid = [attributeDict objectForKey:kAttributeID];
poi.detailId = [attributeDict objectForKey:kAttributeDetailID];
Detail *detail = [self findDetailForID:poi.POIid];
if(detail == nil)
{
detail = [NSEntityDescription
insertNewObjectForEntityForName:#"Detail"
inManagedObjectContext:[(AppDelegate*)[UIApplication sharedApplication].delegate managedObjectContext]];
detail.title = poi.POIid;
detail.subtitle = #"";
detail.detailType = [attributeDict objectForKey:kAttributeType];
}
-(Detail*)findDetailForID:(NSString*)detailID {
NSManagedObjectContext *moc = [[UIApplication sharedApplication].delegate managedObjectContext];
NSEntityDescription *entityDescription = [NSEntityDescription
entityForName:#"Detail" inManagedObjectContext:moc];
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:entityDescription];
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"detailid == %#", detailID];
[request setPredicate:predicate];
NSLog(#"%#", [predicate description]);
NSError *error;
NSArray *array = [moc executeFetchRequest:request error:&error];
if (array == nil || [array count] != 1)
{
// Deal with error...
return nil;
}
return [array objectAtIndex:0];
}
Check out the section titled "Batch Faulting" on the page titled "Core Data Performance" in Xcode's Core Data Programming Guide that Norman linked to in his answer.
Only fetching those managedObjects whose ids are IN a collection (NSSet, NSArray, NSDictionary) of ids of the objects returned by the server may be even more efficient.
NSSet *oids = [[NSSet alloc] initWithObjects:#"oid1", #"oid2", ..., nil];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"oid IN %#", oids];
[oids release];
UPDATE: I worked this tip into a solution for the acani usersView. Basically, after downloading a JSON response of users, the iPhone uses the popular open source JSON framework to parse the response into an NSArray of NSDictionary objects, each representing a user. Then, it makes an NSArray of their uids and does a batch fetch on Core Data to see if any of them already exist on the iPhone. If not, it inserts it. If so, it updates the ones that do exist only if their updated attribute is older than that of the one from the server.
I've gotten all this to work really well, thanks to Norman, who put me on the right path. I'll post my helper class here for others.
Basically, my helper class will look up if an NSManagedObject exists for some ID, and can create it for some ID. This executes quickly enough for me, with 1,000 find/create operations taking around 2 seconds on my iPhone (I also did a few other things there, pure find/create is likely faster).
It does this by caching a dictionary of all the NSManagedObjects, and checking that cache rather than executing a new NSFetchRequest.
A couple of modifications that could help things speed up even further:
1. Get only selected properties for the NSManagedObjects
2. Only get the identifier property for the NSManagedObject into a dictionary, instead of the whole object.
In my performance testing, the single query wasn't the slow part (but with only 1,000 items, I'd expect it to be fast). The slow part was the creation of the items.
#import "CoreDataUniquer.h"
#implementation CoreDataUniquer
//the identifying property is the field on the NSManagedObject that will be used to look up our custom identifier
-(id)initWithEntityName:(NSString*)newEntityName andIdentifyingProperty:(NSString*)newIdProp
{
self = [super init];
if (self != nil) {
entityName = [newEntityName retain];
identifyingProperty = [newIdProp retain];
}
return self;
}
-(NSManagedObject*)findObjectForID:(NSString*)identifier
{
if(identifier == nil)
{
return nil;
}
if(!objectList)
{
NSManagedObjectContext *moc = [(AppDelegate*)[UIApplication sharedApplication].delegate managedObjectContext];
NSEntityDescription *entityDescription = [NSEntityDescription
entityForName:entityName inManagedObjectContext:moc];
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:entityDescription];
NSError *error;
NSArray *array = [moc executeFetchRequest:request error:&error];
objectList = [[NSMutableDictionary dictionary] retain];
for (NSManagedObject* p in array) {
NSString* itemId = [p valueForKey:identifyingProperty];
[objectList setObject:p forKey:itemId];
}
}
NSManagedObject* returnedObject = [objectList objectForKey:identifier];
return returnedObject;
}
-(NSManagedObject*)createObjectForID:(NSString*)identifier
{
NSManagedObject* returnedObject = [NSEntityDescription
insertNewObjectForEntityForName:entityName
inManagedObjectContext:[(AppDelegate*)[UIApplication sharedApplication].delegate managedObjectContext]];
[returnedObject setValue:identifier forKey:identifyingProperty];
[objectList setObject:returnedObject forKey:identifier];
return returnedObject;
}
- (void) dealloc
{
DESTROY(entityName);
DESTROY(identifyingProperty);
[super dealloc];
}
#end
This page provides some help on optimizing performance:
http://developer.apple.com/documentation/Cocoa/Conceptual/CoreData/Articles/cdPerformance.html#//apple_ref/doc/uid/TP40003468-SW1
While not very efficient, why not just build them in-memory with a NSDictionary? Read everything from Core Data into a NSDictionary then merge in your data, replacing everything in Core Data.