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]

Reply via email to