better way to find max date inside big pool of core data objects - cocoa

i have big pool of core date objects (around 10000) and there is too long time doing code according profile:
NSDate *maxExternalChangedDate = [codes valueForKeyPath:#"#max.externalChangedDate"];
is community know better way to found it?
NSString *rateSheetID = [rateSheetAndPrefix valueForKey:#"rateSheetID"];
NSFetchRequest *requestCodesForSale = [[NSFetchRequest alloc] init];
[requestCodesForSale setEntity:[NSEntityDescription entityForName:#"CodesvsDestinationsList"
inManagedObjectContext:self.moc]];
[requestCodesForSale setPredicate:[NSPredicate predicateWithFormat:#"(%K.carrier.GUID == %#)",relationshipName,carrierGUID]];
NSError *error = nil;
NSArray *codes = [self.moc executeFetchRequest:requestCodesForSale error:&error];
if (error) NSLog(#"Failed to executeFetchRequest to data store: %# in function:%#", [error localizedDescription],NSStringFromSelector(_cmd));
NSNumber *count = [codes valueForKeyPath:#"#count.externalChangedDate"];
if (count == 0) {
[requestCodesForSale release];
[pool drain], pool = nil;
return YES;
}
NSDate *maxExternalChangedDate = [codes valueForKeyPath:#"#max.externalChangedDate"];

By using NSFetchRequest and returning NSDictionaryResultType You can use NSExpressionDescription to yeild the results for functions like max() and min().
Sample Code from Apple
NSExpression *keyPathExpression = [NSExpression expressionForKeyPath:#"salary"];
NSExpression *maxSalaryExpression = [NSExpression expressionForFunction:#"max:"
arguments:[NSArray arrayWithObject:keyPathExpression]];
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
[expressionDescription setName:#"maxSalary"];
[expressionDescription setExpression:maxSalaryExpression];
[expressionDescription setExpressionResultType:NSDecimalAttributeType];
[request setPropertiesToFetch:[NSArray arrayWithObject:expressionDescription]];
Check out this doc for more information.
Core Data Programming Guide

I have the same issue.
In theory this should work, but for me it did not.
For some reasons the query crashes with the error that the database is corrupt.
In the end I perfomed a query, where I ordered on my field DESCENDING, and using setFetchLim it:1. Its not perfect, but at least it worked.
Also I made sure the field I use has an index.
Since I have few records, this works fine.
On 30000 records, it might be a problem though.
I followed the IOS documentation, fot fatch a "max:" query, but only got "database corrupted" errors. That is the sample code from Apple fails BADLY.
Googling the internet, it seem the call to setPropertiesToFetch fails in IOS 5+ ??!
I have not found any way around that.
Using a normal query, it worked without any issue.
So I must conclude Apple code is no longer corerct.

Related

Crash on accessing NSManagedObject property after migratePersistentStore: to another URL on OS X 10.9 Mavericks

I have a code that works fine on OS X 10.9 before I migrate the persistent store to another URL. After migration app crashes when I try to execute fetch request and sort by an existing property of fetched items. On OS X 10.8 it works fine before and after migration. Any help is highly appreciated.
Here's the store migration, nothing fancy:
BOOL success = [storeCoordinator migratePersistentStore:store toURL:[NSURL fileURLWithPath:newPath] options:nil withType:NSSQLiteStoreType error:&error];
Migration proceeds successfully, without errors.
Here's the crashing code (which worked fine before migration):
NSManagedObjectContext *moc = [appDelegate managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Item" inManagedObjectContext:moc];
[request setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%K == nil", #"parent"];
[request setPredicate:predicate];
// SORTING BY "sortOrder" PROPERTY CRASHES THE APP
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"sortOrder" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
NSError *error;
NSMutableArray *fetchResults = [moc executeFetchRequest:request error:&error];
Crash details:
Thread 1: EXC_BAD_ACCESS (code=1, address=0x60)
0x00007fff89a64ad0 in _propertyAtIndexForEntityDescription ()
0x00007fff89a631dd in snapshot_get_value_as_object ()
0x00007fff931ea49f in -[NSObject(NSKeyValueCoding) valueForKeyPath:] ()
0x00007fff931e904c in _sortedObjectsUsingDescriptors ()
0x00007fff931e8e69 in -[NSArray(NSKeyValueSorting) sortedArrayUsingDescriptors:] ()
0x00007fff89a459f0 in -[NSManagedObjectContext executeFetchRequest:error:] ()
Update [2]
Thanks to Tom Harrington's question, I was able to narrow the issue down to this:
Update [3]
It appears there's a number of specific keys causing this crash. Out of dozen of different properties of the fetched items, accessing 3 particular properties of type NSDate and 'double' cause the app to crash. All of them has some meaningful value.
There're other properties of type NSString, Boolean, Integer 16, Integer 32 and Binary Data. Some of them are nil, some set to default value, some has some other value. They can be accessed without any error. AND there's one NSDate property without any value including default and it can be accessed without problems also.
Why does it crash? Before migrating store to another URL I can successfully access these properties.
Seems to be a problem with a specific project created a while ago. Probably during these years something got messed in project files and settings.

initWithContentsOfURL often returns nil

NSError *error;
NSString *string = [[NSString alloc]
initWithContentsOfURL:URL
encoding:NSUTF8StringEncoding
error:&error];
When I test this on my iPhone it always works when I have wifi turned on. However when I'm on 3G I often get nil. If I try perhaps 15 times in a row (I have an update button for this) I finally get the desired result.
My question is, is this problem located at the server side or is my code unstable? Should I use a different approach to get a more secure fetch of data?
You haven't provided enough information to give anything but a vague answer, but you do have some options here.
Most importantly, you have an "error" parameter that you should be printing out the results of. There's also a slightly better API you could be using in the NSString class.
Change your code to something like this:
NSError *error = NULL;
NSStringEncoding actualEncoding;
// variable names in Objective-C should usually start with lower case letters, so change
// URL in your code to "url", or even something more descriptive, like "urlOfOurString"
NSString *string = [[NSString alloc] initWithContentsOfURL:urlOfOurString usedEncoding:&actualEncoding error:&error];
if(string)
{
NSLog( #"hey, I actually got a result of %#", string);
if(actualEncoding != NSUTF8StringEncoding)
{
// I also suspect the string you're trying to load really isn't UTF8
NSLog( #"and look at that, the actual encoding wasn't NSUTF8StringEncoding");
}
} else {
NSLog( #"error when trying to fetch from URL %# - %#", [urlOfOurString absoluteString], [error localizedDescription]);
}
I'm now using STHTTPRequest instead. I recommend this library very much, easy to use yet powerful.

avoid duplicate results on Core Data fetch

I have a subclass of the CoreDataTableViewController (subclass of UITAbleViewController dome by the people on Stanford done to link CoreData and TableViews). On this Class, I want to perform a fecth, sorting by an attribute called "definition" and the code which executes it is the following:
- (void)setupFetchedResultsController{
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:self.entity];
request.propertiesToFetch=[NSArray arrayWithObject:#"definition"];
request.returnsDistinctResults=YES;
NSPredicate *predicate1 = [NSPredicate predicateWithFormat:#"%K != nil", #"definition"];
NSPredicate *predicate2 = [NSPredicate predicateWithFormat:#"%K != ''", #"definition"];
NSPredicate *predicate3= [NSPredicate predicateWithFormat:#"%K contains[cd] %#", #"definition", self.seachBar.text];
NSArray *prepredicateArray;
if ([self.seachBar.text length]) {
prepredicateArray = [NSArray arrayWithObjects:predicate1, predicate2, predicate3,nil];
}else {
prepredicateArray = [NSArray arrayWithObjects:predicate1, predicate2,nil];
}
request.predicate=[NSCompoundPredicate andPredicateWithSubpredicates:prepredicateArray];
request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"definition" ascending:YES ]];
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
[self performFetch];
}
If I understood it correctly, setting request.returnsDistinctResults=YES; should avoid fetching duplicates. However it doesn't work and I'm seeing duplicates of this attribute's value.
Is there something I'm missing there? I'd appreciate some pointings there. Thank you in advance.
EDIT: If anyone is having the same issue here, after applying David's answer the resulting fetchedResultsController is just a NSDIctionary with object with only the requested value, which for displaying only purposes is quite fine. One thing I've done in cellForRowAtIndexPath in order to display the results on the cell label is:
Before:
HNMR *hnmr = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text=hnmr.definition;
After:
cell.textLabel.text=[[self.fetchedResultsController objectAtIndexPath:indexPath] valueForKey:#"definition"];
From the documentation of returnsDistinctResults:
This value is only used if a value has been set for propertiesToFetch.
From the documentation of propertiesToFetch:
This value is only used if resultType is set to NSDictionaryResultType.
From the documentation of resultType:
The default value is NSManagedObjectResultType.
This all tells me that the propertiesToFetch is ignored because you haven't set the resultType yourself and the default it to return managed objects instead of dictionaries. Since the propertiesToFetch is ignored the returnsDistinctResults is ignored as well and thus you are still getting duplicates.
Try setting the result type to return dictionaries instead of managed objects.
request.resultType = NSDictionaryResultType;
In addition to David Rönnqvist answer I suggest a useful link (with a sample) on selecting distinct values with Core Data:
core-data-how-to-do-a-select-distinct
Hope that helps.

CoreData predicate latest stored date

I need to get the latest date from coredata
i found a way
NSSortDescriptor * sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"date" ascending:NO];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
[fetchRequest setFetchLimit:1];
so sort them by date and then pick the first
however can this not be done more optimal? this approach looks like a brute force
sort is nlogn, but simple search for the max is n
You can actually ask SQL for just that value, not the object with that value:
NSExpression *date = [NSExpression expressionForKeyPath:#"date"];
NSExpression *maxDate = [NSExpression expressionForFunction:#"max:"
arguments:[NSArray arrayWithObject:maxDate]];
NSExpressionDescription *d = [[[NSExpressionDescription alloc] init] autorelease];
[d setName:#"maxDate"];
[d setExpression:maxSalaryExpression];
[d setExpressionResultType:NSDateAttributeType];
[request setPropertiesToFetch:[NSArray arrayWithObject:d]];
NSError *error = nil;
NSArray *objects = [managedObjectContext executeFetchRequest:request error:&error];
if (objects == nil) {
// Handle the error.
} else {
if (0 < [objects count]) {
NSLog(#"Maximum date: %#", [[objects objectAtIndex:0] valueForKey:#"maxDate"]);
}
}
This is described in more detail under Fetching Managed Objects -> Fetching Specific Values in the CoreData documentation.
On line 2 maxDate expression refers to itself (maxDate).
I assume this must be the "date" variable from the first line.
I guess that's pretty much the best way. I'm not sure whether there is a more efficient way since it has to compare every date anyway to figure out which the oldest is.
Here are 2 other ways:
1) You could work with BOOLs as an attribute of the managed object. (like oldest = 1)
However, you'd have to find a new "oldest" managed object every time you delete one.
2) You could just save the oldest one until it changes. This might save a lot of work if you have to find the oldest managedObject often.
It depends on your application (how many times you insert/remove managed objects and how many times you need the oldest object).

Is there a more memory efficient way to search through a Core Data database?

I need to see if an object that I have obtained from a CSV file with a unique identifier exists in my Core Data Database, and this is the code I deemed suitable for this task:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity;
entity =
[NSEntityDescription entityForName:#"ICD9"
inManagedObjectContext:passedContext];
[fetchRequest setEntity:entity];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"uniqueID like %#", uniqueIdentifier];
[fetchRequest setPredicate:pred];
NSError *err;
NSArray* icd9s = [passedContext executeFetchRequest:fetchRequest error:&err];
[fetchRequest release];
if ([icd9s count] > 0) {
for (int i = 0; i < [icd9s count]; i++) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
NSString *name = [[icd9s objectAtIndex:i] valueForKey:#"uniqueID"];
if ([name caseInsensitiveCompare:uniqueIdentifier] == NSOrderedSame && name != nil)
{
[pool release];
return [icd9s objectAtIndex:i];
}
[pool release];
}
}
return nil;
After more thorough testing it appears that this code is responsible for a huge amount of leaking in the app I'm writing (it crashes on a 3GS before making it 20 percent through the 1459 items). I feel like this isn't the most efficient way to do this, any suggestions for a more memory efficient way? Thanks in advance!
Don't use the like operator in your request predicate. Use =. That should be much faster.
You can specify the case insensitivity of the search via the predicate, using the [c] modifier.
It's not necessary to create and destroy an NSAutoreleasePool on each iteration of your loop. In fact, it's probably not needed at all.
You don't need to do any of the checking inside the for() loop. You're duplicating the work of your predicate.
So I would change your code to be:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:...];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"uniqueID =[c] %#", uniqueIdentifier]];
NSError *err = nil;
NSArray *icd9s = [passedContext executeFetchRequest:fetchRequest error:&err];
[fetchRequest release];
if (error == nil && [icd9s count] > 0) {
return [icd9s objectAtIndex:0]; //we know the uniqueID matches, because of the predicate
}
return nil;
Use the Leaks template in Instruments to hunt down the leak(s). Your current code may be just fine once you fix them. The leak(s) may even be somewhere other than code.
Other problems:
Using fast enumeration will make the loop over the array (1) faster and (2) much easier to read.
Don't send release to an autorelease pool. If you ever port the code to garbage-collected Cocoa, the pool will not do anything. Instead, send it drain; in retain-release Cocoa and in Cocoa Touch, this works the same as release, and in garbage-collected Cocoa, it pokes the garbage collector, which is the closest equivalent in GC-land to draining the pool.
Don't repeat yourself. You currently have two [pool release]; lines for one pool, which gets every experienced Cocoa and Cocoa Touch programmer really worried. Store the result of your tests upon the name in a Boolean variable, then drain the pool before the condition, then conditionally return the object.
Be careful with variable types. -[NSArray count] returns and -[NSArray objectAtIndex:] takes an NSUInteger, not an int. Try to keep all your types matching at all times. (Switching to fast enumeration will, of course, solve this instance of this problem in a different way.)
Don't hide releases. I almost accused you of leaking the fetch request, then noticed that you'd buried it in the middle of the code. Make your releases prominent so that you're less likely to accidentally add redundant (i.e., crash-inducing) ones.

Resources