Paul Heely
Paul Heely

Reputation: 120

NSArrayController without loading a large dataset into an array

I would like to use an NSArrayController to provide data to an NSTableView. The problem I am facing is that I do not want to pre-load all my data into an array and then use the array controllers setContent: method. My data model is a large existing code base that manages millions of records. It contains methods to efficiently return a set of data rows.

Following an example I found on limiting the number of objects in an NSArrayController, I tried subclassing NSArrayController and overriding the arrangedObjects: method to return an array proxy class I wrote. The array proxy class provided count: and objectAtIndex: methods. The object returned by objectAtIndex: is an NSDictionary. When I tried returning my array proxy from the arrangedObjects: method both count: and objectAtIndex: get called, but I also get an unrecognized selector error on my array proxy class for _valueForKeyPath:ofObjectAtIndex:. This looked like a private method, so I did not continue down this path.

I also thought of returning a smaller array of data from arrangedObjects:, but could not figure out how I would determine which rows the NSTableView was trying to display.

Is a datasource the "correct" way to interface with my existing data model or is there some way to make an NSArrayController work?

Upvotes: 4

Views: 963

Answers (1)

Elise van Looij
Elise van Looij

Reputation: 4232

NSArrayController already works, with proxies and indexes and lazy-loading and the whole shabang. Have you tried just using it as-is? If afterwards you feel the need to micro-manage the data-loading, use NSFetchRequest. Subclass NSArrayController and add an initializer along these lines:

+ (id)arrayControllerWithEntityName: (NSString *)entityName error:(NSError **)error
{
    id newInstance = [[[self alloc] initWithContent:nil] autorelease];

    [newInstance setManagedObjectContext:[[NSApp delegate] managedObjectContext]];
    [newInstance setEntityName:entityName];

    [newInstance setAutomaticallyPreparesContent:YES];
    [newInstance setSelectsInsertedObjects:YES];
    [newInstance setAvoidsEmptySelection:YES];
    [newInstance setAlwaysUsesMultipleValuesMarker:YES];

    NSFetchRequest *dontGobbleRequest = [[[NSFetchRequest alloc] init] autorelease];
    //configure the request with fetchLimit and fetchOffset an' all that
    NSError *err;
    if ([newInstance fetchWithRequest:dontGobbleRequest merge:YES error:&err] == NO) {
        //better check docs whether merge:YES is what you want
        if(*error && err) {
            *error = err;
        }
        return nil;
    }

    return newInstance; 
}

You'll have to do some research into the various possibilities and configurations, but you get the picture.

Upvotes: 2

Related Questions