Baffled by NSMutableArray sortUsingDescriptors: exception - ios

The following method:
- (NSMutableArray*) timeSortedBegins {
NSMutableArray* begins = [self.spans valueForKey: #"begin"];
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey: #"cycleOffsetObject" ascending: YES];
[begins sortUsingDescriptors: #[sort]];
return begins;
}
throws this runtime exception:
2014-03-21 14:41:32.482 myValve[1741:60b] -[__NSArrayI sortUsingDescriptors:]: unrecognized selector sent to instance 0x16d7bc20
2014-03-21 14:41:32.484 myValve[1741:60b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayI sortUsingDescriptors:]: unrecognized selector sent to instance 0x16d7bc20'
I have used breakpoints to convince myself that the begins array is indeed full of (two in this case) WEAnchor* objects. And that object implements the following two methods:
- (NSTimeInterval) cycleOffset {
return self.offset + (self.datum ? self.datum.cycleOffset : 0.0);
}
- (NSNumber*) cycleOffsetObject {
return [NSNumber numberWithDouble: self.cycleOffset];
}
To be honest, I only added the cycleOffsetObject wrapper method, because I thought maybe it couldn't work with non object values, I was using initWithKey: #"cycleOffset" before that. I have not declared these in the header file as a property, they're just accessor methods, not state. Is that the problem? If it is, how do you sort by the return value of a given selector? Or is it something head smackingly obvious that I'm just missing?

As #dtrotzjr says, it sounds like your array is an immutable, not a mutable array.
You can either use mutableCopy to create a mutable copy and then sort that copy, or use the NSArray method sortedArrayUsingDescriptors: (which operates on an immutable array, and returns a sorted version of the contents as a second immutable array.)
To use mutableCopy, your code might look like this:
- (NSMutableArray*) timeSortedBegins {
NSMutableArray* begins = [[self.spans valueForKey: #"begin"] mutableCopy];
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey: #"cycleOffsetObject" ascending: YES];
[begins sortUsingDescriptors: #[sort]];
return begins;
}

Check that [self.spans valueForKey: #"begin"] is actually an NSMutableArray before casting it. The error message indicates that the pointer is actually an NSArray

Related

How to populate a UIPickerView with results from a NSFetchRequest using Core Data

I have a UIPickerView that I am trying populate with the results from a NSFetchRequest pulling data from a managedObjectContext. When I initialize my UIPickerView with the following, KCModalPickerView *pickerView = [[KCModalPickerView alloc] initWithValues:_userNames]; Xcode doesn't throw and warnings or errors, but when I build and run the app I am getting the following error.
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Account copyWithZone:]: unrecognized selector sent to instance 0x7a60ec70'
* First throw call stack:
Now before you say this error is due to me not implementing the copyWithZone method in my vc, I want to point out that nowhere in my class files am I using the keyword copy
The method that I was told that is causinging the crash belongs to the KCModalPicker class implementation file. And the method looks like the following,
// edit this method
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
return [self.values objectAtIndex:row];
}
What do I need to change / edit / add to prevent this app from crashing?
Update
_usernames is a NSArray ...the results look like the following,
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Account" inManagedObjectContext:_managedObjectContext];
[fetchRequest setEntity:entity];
fetchRequest.propertiesToFetch = [NSArray arrayWithObject:[[entity propertiesByName] objectForKey:#"username"]];
NSError *error = nil;
NSArray _usernames = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
KCModalPickerView expects an array of NSString, you give it an array of Account. The framework tries to copy your instance because it thinks it's an NSString, which conforms to the NSCopying protocol and implements copyWithZone:. Your object does not, and there will be a [Account copyWithZone:]: unrecognized selector sent to instance exception.
Simply create an array of NSStrings by using the appropriate attribute from your Core Data object.
There are probably smarter ways for this, but this would be the obvious solution:
NSMutableArray *names = [NSMutableArray arrayWithCapacity:[_usernames count]];
for (Account *account in _usernames) {
NSString *accountName = account.name;
if (!accountName) {
accountName = #"<Unknown Account>";
}
[names addObject:accountName];
}
KCModalPickerView *pickerView = [[KCModalPickerView alloc] initWithValues:names];
I just saw that you have set propertiesToFetch. Additionally you have to set resultsType of the fetchRequest to NSDictionaryResultType. In this case executeFetchRequest:error: returns an array of dictionaries. And you should be able to use NSArray *names = [_usernames valueForKey:#"username"]; instead of the for loop.

Error thrown when using sortingUsingDescriptors, NSSortDescriptor and sortDescriptorWithKey methods on an NSMutable array

I created an array of objects, and one of the properties of the object is "rank". I want to sort the array by the rank value of each object in it.
It throws the following error:
-[NSSortDescriptor count]: unrecognized selector sent to instance 0x10a0537d0
Here's my method call on the array of objects:
[_objects sortUsingDescriptors:[NSSortDescriptor sortDescriptorWithKey:#"rank" ascending:YES selector:#selector(compare:)]];
And here's the relevant code from the class that the objects in the array belong to:
#synthesize rank;
- (void)initWithRank:(int)rankNum Name:(NSString*)nameString URL:(NSString*)urlString
{
self.rank = [NSNumber numberWithInt:rankNum];
self.name = nameString;
self.url = urlString;
}
As you can see, "rank" is an NSNumber, and the NSNumber class has a method called "compare:" that's supposed to compare NSNumbers by their values, so I don't understand why it's telling me the selector is unrecognized. Thanks in advance for any help.
sortUsingDescriptors: expects an NSArray of sort descriptors as its first argument:
[_objects sortUsingDescriptors:#[[NSSortDescriptor sortDescriptorWithKey:#"rank" ascending:YES selector:#selector(compare:)]]];
Or a bit longer:
NSSortDescriptor *descriptor = [NSSortDescriptor sortDescriptorWithKey:#"rank" ascending:YES selector:#selector(compare:)];
[_objects sortUsingDescriptors:#[descriptor]];

Sorting an NSMutableArray by a key value throws exception

I am trying to sort an NSMutableArray with the following structure:
(
{
Code = "390954-150";
Size = "35";
},
{
Code = 790540MSO;
Size = "30";
}
)
I am trying to sort basing on the Code value, the NSMutableArray is called arrayProduct:
//Sort array by Code
NSSortDescriptor *aSortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"Code" ascending:YES];
[arrayProduct sortUsingDescriptors:[NSArray arrayWithObject:aSortDescriptor]];
//
However the above code throws the following exception:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFArray replaceObjectAtIndex:withObject:]: mutating method sent to immutable object'
Your arrayProduct looks to be NSArray.
Try converting it into NSMutableArray and :
NSMutableArray *mutableProducts = [NSMutableArray arrayWithArray:arrayProduct];
[mutableProducts sortUsingDescriptors:[NSArray arrayWithObject:aSortDescriptor]];

ios - NSFetchResultsController error with sortDescriptors

I have a Table View Controller with a list of Formations handled by a fetchResultsController.
Here's how my core data entities looks like :
I try to sort my fetchResultsController by dateRangelike this :
// |fetchedResultsController| custom setter
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:self.fetchRequest managedObjectContext:self.mainManagedObjectContext sectionNameKeyPath:#"dateRange" cacheName:kFormationsFetchedCacheName];
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
}
// |fetchRequest| custom setter
- (NSFetchRequest *)fetchRequest {
if (_fetchRequest != nil) {
return _fetchRequest;
}
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"student == %#", self.currentStudent];
NSSortDescriptor *dateDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"dateRange" ascending:NO];
NSSortDescriptor *nameDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"name" ascending:NO];
_fetchRequest = [[NSFetchRequest alloc] initWithEntityName:kBSFormation];
_fetchRequest.predicate = predicate;
_fetchRequest.sortDescriptors = [NSArray arrayWithObjects: dateDescriptor, nameDescriptor, nil];
return _fetchRequest;
}
Everything is fine when i try to add the very first Formation, but for the next ones, i have these errors:
2013-01-30 22:43:08.370 [7202:c07] -[BSDateRange compare:]: unrecognized selector sent to instance 0x81781a0
2013-01-30 22:43:08.371 [7202:c07] CoreData: error: Serious application error. Exception was caught during Core Data change processing. This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification. -[BSDateRange compare:]: unrecognized selector sent to instance 0x81781a0 with userInfo (null)
2013-01-30 22:43:08.372 [7202:c07] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[BSDateRange compare:]: unrecognized selector sent to instance 0x81781a0'
If i comment this line : NSSortDescriptor *dateDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"dateRange" ascending:NO];, it's working but my table view is messy as the sectionNameKeyPath is set to dateRange
Someone is figuring out what's the problem here ? :/
You're telling it to sort based on the dateRange relationship. But dateRange is a relationship to BSDateRange, and how does Core Data compare those? Should it use from, or maybe to, or some combination of those? You can't just tell it to sort on an object like that, because it's not obvious how sorting should work.
Instead, first figure out what sorting even means. Then modify your sort descriptor appropriately. For example if you decide that sorting depends on the from value, change the sort descriptor to use the key path to from:
NSSortDescriptor *dateDescriptor = [NSSortDescriptor
sortDescriptorWithKey:#"dateRange.from"
ascending:NO];
If you need to sort based on both from and to, use more than one sort descriptor.

sortedArrayWithSelector and NSDictionnary on iOS

I have an array of NSDictionnary and I would like to sort them by an int value (the key is named "age").
For exemple :
Dic1 : age = 30, sex = male;
Dic2 : age = 24, sex = male;
Array : Dic1, Dic2;
Now with a compare method I would lik to have :
Array : Dic2, Dic1;
I tried lots of things but doesn't work. I as using NSSortDescriptor but it's only available since iOS 4.0 :(
Have you got a solution ?
Thanks !
EDIT :
I wrote a compare method on NSDictionary category :
- (NSComparisonResult)compareGood:(NSDictionary *)otherObject
{
return [[self objectForKey:#"age"] compare:[otherObject objectForKey:#"age"]];
}
But I have this crash error : Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '* -[NSCFNumber compare:]: nil argument'
The documentation for NSSortDescriptor says:
Available in iOS 2.0 and later.
Likewise for the NSMutableArray method -sortUsingDescriptors:. You should be able to do this:
NSSortDescriptor *descriptor = [[[NSSortDescriptor alloc] initWithKey:#"age"
ascending:YES]
autorelease];
[myArray sortUsingDescriptors:[NSArray arrayWithObject:descriptor];
or, if myArray isn't mutable, do this for the second line:
NSArray *sortedArray = [myArray sortedArrayUsingDescriptors:[NSArray arrayWithObject:descriptor]];

Resources