NSArrayController initialization

2020-05-24 04:56发布

问题:

I am having trouble getting an core-data backed NSArrayController to work properly in my code. Below is my code:

pageArrayController = [[NSArrayController alloc] initWithContent:nil];
    [pageArrayController setManagedObjectContext:[self managedObjectContext]];
    [pageArrayController setEntityName:@"Page"];
    [pageArrayController setAvoidsEmptySelection:YES];
    [pageArrayController setPreservesSelection:YES];
    [pageArrayController setSelectsInsertedObjects:YES];
    [pageArrayController setClearsFilterPredicateOnInsertion:YES];
    [pageArrayController setEditable:YES];
    [pageArrayController setAutomaticallyPreparesContent:YES];
    [pageArrayController setSortDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"index" ascending:YES]]];
    BOOL result = [pageArrayController setSelectionIndex:0];

When I attempt to call setSelectionIndex:, it returns YES, indicating that the selection has been successfully changed. However, any subsequent getSelectionIndex calls to the pageArrayController object returns NSNotFound.

What I don't understand is that if I put the NSArrayController into a NIB, and allow the NIB file to perform the initialization (with all of the same attributes in Interface Builder), the NSArrayController works correctly.

Why is there a difference in behavior? Does the NIB file initialize these types of objects in a special way? Is my initialization of the NSArrayController incorrect?

Any help is appreciated. Thanks.

回答1:

Yes, nibs do initialize objects in a special way and sometimes it can be hard to figure out how to replicate that. I struggled with this too and finally found the answer in Apple's Core Data Programming Guide >> Core Data and Cooca Bindings >> Automatically Prepares Content Flag (thanks to Dave Fernandes on the Cocoa Dev list). The answer is that if you initialize an arraycontroller with nil content, you need to perform a fetch as well.

BOOL result;
NSArrayController *pageArrayController = [[NSArrayController alloc] initWithContent:nil];
[pageArrayController setManagedObjectContext:[self managedObjectContext]];
[pageArrayController setEntityName:@"Page"];
NSError *error;
if ([pageArrayController fetchWithRequest:nil merge:YES error:&error] == NO) 
     result = NO;
else
{
     //do all that other pageArrayController configuration stuff
     result = [pageArrayController setSelectionIndex:0];
}

BTW, [NSSortDescriptor sortDescriptorWithKey:@"index" ascending:YES]] raises a warning.



回答2:

As far as why there may be a difference in behavior:

  1. Nib files store serialized objects using NSCoder.
  2. You are probably using binding on the IB side of things, where in your code you are setting the managed object context directly using a set method.

Maybe you could try something like the following in your code:

[pageArrayController bind:@"managedObjectContext"
                 toObject:self
              withKeyPath:@"managedObjectContext"
                  options:nil];

I don't have Xcode near by otherwise I would try somethings. Hopefully this gives you some clues to get you going in the right direction.



回答3:

From where are you creating/configuring your array controller? The Core Data stack may not be ready yet, therefore your call to [self managedObjectContext] may be returning nil.

Also, why are you creating it programmatically if you can do it just fine with Interface Builder? The tool is there and works well (and eliminates many possible coding errors), so unless you have a good reason not to use it, you're not doing yourself any favors.