how to sort an NSMutableArray that contains an instance of NSNull
I’ve included a placeholder [NSNull null] in my combobox datasource array so I can allow the user to select “none” without actually storing a blank object. The problem is that when a real object is added to the datasource array, I need to sort the array:
[self.mutableArrayOfStrings sortUsingSelector:@selector(caseInsensitiveCompare:)];
This line produces a SIGKILL.
I’ve got the same problem with the companion array of NSManagedObject, which also has a placeholder NSNull:
NSSortDescriptor *sortDescriptorName = [[NSSortDescriptor alloc] initWithKey:@“name” ascending:YES selector:@selector(caseInsensitiveCompare:)];
NSArray *sortDescriptorsNames = [[NSArray alloc] initWithObjects: sortDescriptorName, nil];
[self.mutableArrayOfMOs sortUsingDescriptors:sortDescriptorsNames];
If mutableArrayOfMOs contains an NSNull开发者_如何学编程 object, this line also produces a SIGKILL.
Of course, I could copy the non-null objects out to a separate array, sort it, re-insert the null object, and assign it to the array property — but that will clutter my code. Shouldn’t there be ways of sorting that don’t choke on NSNull? After all, Apple has provided NSNull explicitly to allow us to include it in arrays.
The main issue is what does NSNull sort as -is it before or after a known element? - there is no general answer - so Apple cannot a general purpose method.
In your case you are sorting by sortUsingDescriptors - this takes the property "name" from the objects in the array - what is the value of the property "name" for NSNull ?
So using sortUsingDescriptors you have to remove NSNull as you describe.
If you want to keep the NSNull in the array you can try sorting using another way - sortUsingFunction:context: will allow you to write a compare function that can deal with NSNull. The Apple Code only has to deal with known objects and not what they contain.
Here’s what I came up with as a result of itaiferber’s suggestion (and using Apple’s Foundation Data Types Reference):
I put this in the h file:
typedef NSComparisonResult (^NSComparator)(id string1, id string2);
I added this method:
- (NSArray *) sortNullsToTopInStringArray:(NSArray *)array {
NSArray *sortedArray = [array sortedArrayUsingComparator: ^(id string1, id string2) {
// First test for null object. It should go to top (beginning) of list.
if ([string1 class] == [NSNull class])
return (NSComparisonResult)NSOrderedAscending;
if ([string2 class] == [NSNull class])
return (NSComparisonResult)NSOrderedDescending;
// For non-null strings, use regular NSString compare method, and wrap it in NSComparisonResult.
switch ([string1 caseInsensitiveCompare:string2]) {
case NSOrderedAscending:
return (NSComparisonResult)NSOrderedAscending;
case NSOrderedSame:
return (NSComparisonResult)NSOrderedSame;
case NSOrderedDescending:
return (NSComparisonResult)NSOrderedDescending;
default:
return (NSComparisonResult)NSOrderedAscending;
}
}];
return sortedArray;
}
and used it here:
NSArray *sortedArray = [self sortNullsToTopInStringArray:self.mutableArrayOfStrings];
NSMutableArray *sortedMutableArray = [sortedArray mutableCopy];
self.mutableArrayOfStrings = sortedMutableArray;
[sortedMutableArray release];
Sorting the NSManagedObjects can be done the same way, but I’d have to provide different methods for each entity type, in order to extract the strings.
Thanks again to Mark and itaiferber.
精彩评论