Hello list,
I'm experiencing a strange behavior on the part of NSFetchedResultsController
and I can't seem to find why, although I suspect it's something utterly simple
that I'm just not seeing.
Here's the setup. I have a list of some 20 keys the user can choose from to
sort some experimental data by, and these keys are represented in a core data
model. The custom subclass of NSManagedObject that encapsulates each key is
called SortDescriptorCD and is defined as follows (other non-relevant stuff has
been removed):
// SortDescriptorCD.h
#import <CoreData/CoreData.h>
// class statements
@interface SortDescriptorCD: NSManagedObject
{}
@property (readwrite, nonatomic, retain) NSNumber* index;
@property (readwrite, nonatomic, retain) NSString* title;
@property (readonly, nonatomic, retain) id indexOrTitle;
@property (readwrite, nonatomic, retain) NSNumber* canAppearOnList;
@property (readwrite, nonatomic, retain) NSNumber* doesAppearOnList;
@property (readonly, nonatomic, retain) NSNumber* canAndDoesAppearOnList;
// other property declarations
@end
// SortDescriptorCD.m
#import "SortDescriptorCD.h"
// other import statements
@implementation SortDescriptorCD
@dynamic index;
@dynamic title;
@dynamic canAppearOnList;
@dynamic doesAppearOnList;
// other dynamic declarations
- (id) indexOrTitle;
{
if ([self.canAndDoesAppearOnList isEqualToNumber:
[NSNumber numberWithBool: YES]])
{
return self.index;
}
else
{
return self.title;
}
}
- (NSNumber*) canAndDoesAppearOnList;
{
BOOL can = [self.canAppearOnList boolValue];
BOOL does = [self.doesAppearOnList boolValue];
return [NSNumber numberWithBool: (can && does)];
}
@end
Title isn't the actual key, but a human-readable version of it. These keys are
sometimes not applicable to the data being shown, hence the canAppearOnList
boolean. If they can, then whether or not they actually do is user-defined and
that choice is stored in doesAppearOnList.
The terminology may seem a bit confusing because there are other things not
being shown here. For instance, there is another pair of attributes, canSort
and doesSort. The idea is that for those keys that canAppearOnList the user can
select whether or not they want to see the information they represent
(doesAppearOnList) and, additionally, whether that information is sorted or not
(doesSort), assuming that it can be (canSort). And if it can be sorted and if
the user chooses to sort by that key, whether it's sorted ascending or
descending.
When the user de-selects a sorting key, its title gets grayed-out and its cell
gets moved to a separate part of the tableview where it appears (it's not a
separate tableview section, though I'm considering doing it that way). The
selected part is sorted by index to allow the user to re-arrange the sorting
order of the selected keys. The non-selected part is sorted by title. Tapping a
row in one part moves its cell to the other part.
Now, the two attributes indexOrTitle and canAndDoesAppearOnList are defined in
the core data model as transformable (optional and using the default
transformer). The NSFetchedResultsController is defined as follows:
- (NSFetchedResultsController*) sortDescriptorCDs;
{
NSManagedObjectContext* context = self.managedObjectContext;
NSEntityDescription* entity = [NSEntityDescription
entityForName: @"SortDescriptorCD"
inManagedObjectContext: context];
NSSortDescriptor* sortByCanAndDoesAppearOnList = [[NSSortDescriptor alloc]
initWithKey: @"canAndDoesAppearOnList" ascending: NO];
// Replacing @"indexOrTitle" with @"index" or @"title" works fine.
NSSortDescriptor* sortByIndexOrTitle = [[NSSortDescriptor alloc]
initWithKey: @"indexOrTitle" ascending: YES];
NSArray* sortDescriptors = [[NSArray alloc] initWithObjects:
sortByCanAndDoesAppearOnList, sortByIndexOrTitle, nil];
[sortByCanAndDoesAppearOnList release];
[sortByIndexOrTitle release];
NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity: entity];
[fetchRequest setSortDescriptors: sortDescriptors];
[fetchRequest setFetchBatchSize: 20];
[sortDescriptors release];
NSFetchedResultsController* frc = [[NSFetchedResultsController alloc]
initWithFetchRequest: fetchRequest
managedObjectContext: context
sectionNameKeyPath: nil
cacheName: nil];
[fetchRequest release];
NSError* error = nil;
if (! [frc performFetch: &error])
{
// TODO - Handle fetching error.
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
return [frc autorelease];
}
Note the comment
// Replacing @"indexOrTitle" with @"index" or @"title" works fine.
which makes me confident that sortDescriptorCDs is implemented correctly. By
working fine I mean that the tableview does display all the keys sorted
correctly either by their indices or by their titles, depending on the
replacement.
However, using @"indexOrTitle" seems to produce a random order for the fetched
results (random in that there's no obvious pattern to the resulting sort
order). Moreover, I verified that -indexOrTitle is not invoked at all in this
case. -canAndDoesAppearOnList is invoked, as expected, once per entity when the
fetch is performed.
Even stranger is that I have another situation similar to this one in the same
project and it works correctly. The various attributes are defined and
everything is implemented exactly the same way; the only differences I can see
are in the names of the various attributes and classes.
Here's the code for the situation that works without a glitch (again,
non-relevant stuff has been removed):
// ExperimentCD.h
#import <CoreData/CoreData.h>
// class statements
@interface ExperimentCD: NSManagedObject
{}
@property (readwrite, nonatomic, retain) NSString* displayName;
@property (readwrite, nonatomic, retain) NSNumber* userSelected;
@property (readwrite, nonatomic, retain) NSNumber* userOrderIndex;
@property (readonly, nonatomic, retain) id userOrderIndexOrDisplayName;
// other property declarations
@end
// interface for CoreDataGeneratedAccessors
// ExperimentCD.m
#import "ExperimentCD.h"
// other import statements
@implementation ExperimentCD
@dynamic displayName;
@dynamic userSelected;
@dynamic userOrderIndex;
// other dynamic statements
- (id) userOrderIndexOrDisplayName;
{
if ([self.userSelected isEqualToNumber: [NSNumber numberWithBool: YES]])
{
return self.userOrderIndex;
}
else
{
return self.displayName;
}
}
@end
- (NSFetchedResultsController*) allExperimentCDsFRC;
{
NSManagedObjectContext* context = self.managedObjectContext;
NSEntityDescription* entity = [NSEntityDescription
entityForName: @"ExperimentCD"
inManagedObjectContext: context];
NSSortDescriptor* sortByUserSelected = [[NSSortDescriptor alloc]
initWithKey: @"userSelected" ascending: NO];
NSSortDescriptor* sortByUserOrderIndexOrDisplayName =
[[NSSortDescriptor alloc] initWithKey:
@"userOrderIndexOrDisplayName" ascending: YES];
NSArray* sortDescriptors = [[NSArray alloc] initWithObjects:
sortByUserSelected, sortByUserOrderIndexOrDisplayName, nil];
[sortByUserSelected release];
[sortByUserOrderIndexOrDisplayName release];
NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity: entity];
[fetchRequest setSortDescriptors: sortDescriptors];
[fetchRequest setFetchBatchSize: 10];
[sortDescriptors release];
NSFetchedResultsController* frc = [[NSFetchedResultsController alloc]
initWithFetchRequest: fetchRequest
managedObjectContext: context
sectionNameKeyPath: nil
cacheName: nil];
[fetchRequest release];
NSError* error = nil;
if (! [frc performFetch: &error])
{
// TODO - handle fetching error
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
return [frc autorelease];
}
In this second case, the user can select a group of experiments to monitor and
it's the same mechanism as before. Tapping on the row for a selected experiment
deselects it and vice-versa, and the user can rearrange the order of the rows
for the selected experiments. The selected ones are sorted by index, the
unselected ones are sorted by their display names.
This second case works just fine. I copied the implementation of this case when
writing the code for the first case, and the corresponding attributes in the
core data model are all defined similarly, so I can't figure out why the first
case isn't working. The biggest clue is that -indexOrTitle isn't being invoked,
but I can't see why not.
Can someone please shed some light on this?
Thanks in advance.
WT_______________________________________________
Cocoa-dev mailing list ([email protected])
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com
This email sent to [email protected]