Запись источника данных представления схемы

Представления схемы поддерживают делегата источника данных в дополнение к стандартному объекту делегата. Источник данных реализует NSOutlineViewDelegate протокол и предоставляет данные и информацию о тех данных к представлению схемы, и ответственен за управление данными. Несмотря на то, что представление схемы не требует делегата, оно должно иметь источник данных для отображения информации.

Требования источника данных

Как экземпляр NSTableView, экземпляр NSOutlineView получает все его данные от объекта, который Вы обеспечиваете, названный его источником данных. Ваш объект источника данных может сохранить записи всегда, Вы выбираете, но он должен быть в состоянии идентифицировать их их позицией в иерархии через NSOutlineViewDataSource протокол (до OS X v10.6, это было неофициальным протоколом —NSOutlineViewDataSource). Источник данных должен минимально реализовать методы доступа к данным (outlineView:child:ofItem:, outlineView:isItemExpandable:, outlineView:numberOfChildrenOfItem:, и outlineView:objectValueForTableColumn:byItem:). Указать корневой элемент в любом из этих методов, nil отправляется как параметр элемента метода. Если Вы хотите позволить пользователю редактировать элементы, необходимо также реализовать метод для изменения значения атрибута (outlineView:setObject:forTableColumn:byItem:).

Обычно сам источник данных управляет набором объектов модели, каждый из которых знает, каково их значение, представляют ли они вершину, и сколько (если таковые имеются) дочерние объекты они имеют.

Управление источником данных и управление памятью

Точно так же, как табличное представление представление схемы использует источник данных исключительно для получения информации. Представлению схемы не принадлежит его источник данных. Точно так же этому не принадлежат объекты, которые это получает от источника данных — если они будут выпущены, то Ваше приложение, вероятно, откажет, если Вы не скажете представлению схемы перезагружать свои данные.

Источник данных является объектом контроллера, и Вы ответственны за обеспечение, что он не освобожден, прежде чем представление схемы закончено с ним (обычно, источник данных является объектом, таким как объект документа в основанном на документе приложении, таким образом, нет никакой дополнительной работы, чтобы сделать). Источник данных поочередно ответственен за сохранение всех объектов, которые это обеспечивает для представления схемы и обновления представления схемы, когда существует изменение в модели. Поэтому не безопасно выпустить корневой элемент — или любые дочерние элементы — пока Вы больше не выводите на экран его в представлении схемы. Если необходимо избавиться от корневого элемента, то необходимо гарантировать, что ссылки на него аннулированы, и что представление схемы обновляется, чтобы гарантировать, что никакая попытка не предпринята для отображения других элементов, от которых, возможно, также избавились, как в следующем примере.

    [rootItem release];
    rootItem = nil;
    [outlineView reloadData];

Демонстрационная реализация источника данных

Следующий пример показывает, что реализация класса источника данных, используемого в сочетании с представлением схемы для отображения содержания файловой системы, и класса раньше, представляла записи в файловой системе. Перечисление 1 показывает реализацию класса источника данных. Перечисление 2 показывает, что реализация класса раньше представляла записи в файловой системе. Одиночный элемент rootItem экземпляр используется в качестве корневого объекта в примере в Перечислении 1.

  Реализация перечисления 1 источника данных представления схемы

@implementation DataSource
// Data Source methods
 
- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
 
    return (item == nil) ? 1 : [item numberOfChildren];
}
 
 
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item {
    return (item == nil) ? YES : ([item numberOfChildren] != -1);
}
 
 
- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item {
 
    return (item == nil) ? [FileSystemItem rootItem] : [(FileSystemItem *)item childAtIndex:index];
}
 
 
- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item {
    return (item == nil) ? @"/" : [item relativePath];
}
 
@end

  Реализация перечисления 2 элемента источника данных представления схемы

@interface FileSystemItem : NSObject
{
    NSString *relativePath;
    FileSystemItem *parent;
    NSMutableArray *children;
}
 
+ (FileSystemItem *)rootItem;
- (NSInteger)numberOfChildren;// Returns -1 for leaf nodes
- (FileSystemItem *)childAtIndex:(NSUInteger)n; // Invalid to call on leaf nodes
- (NSString *)fullPath;
- (NSString *)relativePath;
 
@end
 
 
@implementation FileSystemItem
 
static FileSystemItem *rootItem = nil;
static NSMutableArray *leafNode = nil;
 
+ (void)initialize {
    if (self == [FileSystemItem class]) {
        leafNode = [[NSMutableArray alloc] init];
    }
}
 
- (id)initWithPath:(NSString *)path parent:(FileSystemItem *)parentItem {
    self = [super init];
    if (self) {
       relativePath = [[path lastPathComponent] copy];
       parent = parentItem;
       }
    return self;
}
 
 
+ (FileSystemItem *)rootItem {
    if (rootItem == nil) {
        rootItem = [[FileSystemItem alloc] initWithPath:@"/" parent:nil];
    }
    return rootItem;
}
 
 
// Creates, caches, and returns the array of children
// Loads children incrementally
- (NSArray *)children {
 
    if (children == nil) {
        NSFileManager *fileManager = [NSFileManager defaultManager];
        NSString *fullPath = [self fullPath];
        BOOL isDir, valid;
 
        valid = [fileManager fileExistsAtPath:fullPath isDirectory:&isDir];
 
        if (valid && isDir) {
            NSArray *array = [fileManager contentsOfDirectoryAtPath:fullPath error:NULL];
 
            NSUInteger numChildren, i;
 
            numChildren = [array count];
            children = [[NSMutableArray alloc] initWithCapacity:numChildren];
 
            for (i = 0; i < numChildren; i++)
            {
                FileSystemItem *newChild = [[FileSystemItem alloc]
                                   initWithPath:[array objectAtIndex:i] parent:self];
                [children addObject:newChild];
                [newChild release];
            }
        }
        else {
            children = leafNode;
        }
    }
    return children;
}
 
 
- (NSString *)relativePath {
    return relativePath;
}
 
 
- (NSString *)fullPath {
    // If no parent, return our own relative path
    if (parent == nil) {
        return relativePath;
    }
 
    // recurse up the hierarchy, prepending each parent’s path
    return [[parent fullPath] stringByAppendingPathComponent:relativePath];
}
 
 
- (FileSystemItem *)childAtIndex:(NSUInteger)n {
    return [[self children] objectAtIndex:n];
}
 
 
- (NSInteger)numberOfChildren {
    NSArray *tmp = [self children];
    return (tmp == leafNode) ? (-1) : [tmp count];
}
 
 
- (void)dealloc {
    if (children != leafNode) {
        [children release];
    }
    [relativePath release];
    [super dealloc];
}
 
@end