NSTreeview 复制我的对象
NSTreeview Duplicating my objects
我在 OS X 开发中的早期步骤。
NSTreeview 绑定到一个包含各种自定义代理对象的数组 class。其中一些对象绑定到我的核心数据存储,因此我的侧边栏包含多个不同 NSManagedObject
实体的列表。由于某种原因,moc 实体是重复的,每个都列出了两次。像这样:
NSTreeController 绑定到 NSArray,sidebarItems,这里填充:
- (void)populateOutlineContents
{
// hide the outline view - don't show it as we are building the content
[self.myOutlineView setHidden:YES];
BrowserItem *dashboardItem = [BrowserItem itemWithTitle:@"Home"];
dashboardItem.nodeIcon = [NSImage imageNamed:@"home"];
BrowserItem *todayItem = [BrowserItem itemWithTitle:@"Today"];
BrowserItem *thisWeekItem = [BrowserItem itemWithTitle:@"This Week"];
BrowserItem *separatorItem = [BrowserItem itemWithTitle:nil];
// Setup Projects
self.projectsSidebarGroup = [BrowserItem itemWithTitle:PROJECTS_NAME];
self.projectsSidebarGroup.childItemTitleKeypath = @"projectName";
[self.projectsSidebarGroup bindChildItemsToArrayKeypath:@"projectsArrayController.arrangedObjects" onObject:self];
// Setup Crewmen
self.crewmenSidebarGroup = [BrowserItem itemWithTitle:CREWMEN_NAME];
self.crewmenSidebarGroup.childItemTitleKeypath = @"fullName";
[self.crewmenSidebarGroup bindChildItemsToArrayKeypath:@"crewmenArrayController.arrangedObjects" onObject:self];
self.sidebarItems = @[dashboardItem, todayItem, thisWeekItem,separatorItem, self.projectsSidebarGroup, self.crewmenSidebarGroup];
[self.myOutlineView setHidden:NO]; // we are done populating the outline view content, show it again
}
在 viewDidLoad 中,我为每个组设置了核心数据绑定和 NSArrayControllers:
[self populateOutlineContents];
NSPredicate *ActiveProjectsFilter = [NSPredicate predicateWithFormat:@"inactive == NO"];
NSSortDescriptor *sortByName = [[NSSortDescriptor alloc] initWithKey:@"projectName" ascending:YES];
self.projectsArrayController = [self controllerForEntity:@"Project" filter:ActiveProjectsFilter sortBy:@[sortByName]];
NSPredicate *activeCrewmenFilter = [NSPredicate predicateWithFormat:@"Active == YES"];
NSSortDescriptor *sortByLast = [[NSSortDescriptor alloc] initWithKey:@"nameLast" ascending:YES];
NSSortDescriptor *sortByFirts = [[NSSortDescriptor alloc] initWithKey:@"nameFirst" ascending:YES];
self.crewmenArrayController = [self controllerForEntity:@"WorkMan" filter:activeCrewmenFilter sortBy:@[sortByLast, sortByFirts]];
实际控制人设置在这里:
-(NSArrayController *)controllerForEntity:(NSString *)aEntityName filter:(NSPredicate *)filter sortBy:(NSArray *)sortDescriptors
{
NSArrayController *controller = [[NSArrayController alloc] init];
controller.managedObjectContext = self.managedObjectContext;
controller.entityName = aEntityName;
controller.automaticallyRearrangesObjects = YES;
controller.automaticallyPreparesContent = YES;
if (filter) {
controller.fetchPredicate = filter;
}
if (sortDescriptors) {
controller.sortDescriptors = sortDescriptors;
}
NSError *error;
if (![controller fetchWithRequest:nil merge:NO error:&error])
{
ALog(@"Error fetching %@", aEntityName);
}
return controller;
}
我检查了我的 NSArrayControllers 和 arrangedObjects 的状态,那里只有预期数量的项目。实际上不存在重复项,那么 OutlineView 中的重复项来自哪里?
我的代理对象,以防有帮助:
#import "BrowserItem.h"
@implementation BrowserItem
+(instancetype)itemWithTitle:(NSString *)title
{
BrowserItem *item = [[BrowserItem alloc] init];
[item setTitle:title];
return item;
}
- (instancetype)init
{
if (self = [super init])
{
self.children = [NSMutableArray array];
self.modelToItemMapping = [NSMapTable weakToStrongObjectsMapTable];
self.childItemsArrayController = [[NSArrayController alloc] init];
[self.childItemsArrayController addObserver:self forKeyPath:@"arrangedObjects" options:NSKeyValueObservingOptionPrior context:NULL];
}
return self;
}
- (void)bindChildItemsToArrayKeypath:(NSString*)keypath onObject:(id)object;
{
[self.childItemsArrayController bind:@"contentArray" toObject:object withKeyPath:keypath options:nil];
self.isGroup = YES;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:@"arrangedObjects"])
{
[self willChangeValueForKey:@"children"];
NSMutableArray *watchableChildren = [self mutableArrayValueForKey:@"children"];
[watchableChildren removeAllObjects];
for (id modelItem in self.childItemsArrayController.arrangedObjects)
{
BrowserItem *browserItem = [self.modelToItemMapping objectForKey:modelItem];
if (!browserItem)
{
browserItem = [BrowserItem itemWithTitle:[modelItem valueForKey:self.childItemTitleKeypath]];
[self.modelToItemMapping setObject:browserItem forKey:modelItem];
}
[watchableChildren addObject:browserItem];
}
[self didChangeValueForKey:@"children"];
}
}
@end
------------更新---------
所以,我从 NSMutableArrayForKey
更改为创建一个新的 NSMutableArray
并在我的所有对象都设置好后替换子数组。但是,这消除了重复列表,仍然存在冲突。我在其中放置了一些调试语句和计数器,我可以看到例程在每个组上 运行 3 或 4 次。这似乎是不必要的。我想它没有重复,因为以前的结果现在被最新的 运行 取代了。还是想解决而不是原样离开
这是修改后的观察者:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:@"arrangedObjects"])
{
[self willChangeValueForKey:@"children"];
NSMutableArray *replacementChildren = [NSMutableArray array];
for (id modelItem in self.childItemsArrayController.arrangedObjects)
{
BrowserItem *browserItem = [self.modelToItemMapping objectForKey:modelItem];
if (!browserItem)
{
browserItem = [BrowserItem itemWithTitle:[modelItem valueForKey:self.childItemTitleKeypath]];
[self.modelToItemMapping setObject:browserItem forKey:modelItem];
[self.itemToModelMapping setObject:modelItem forKey:browserItem];
}
[replacementChildren addObject:browserItem];
}
_children = replacementChildren;
[self didChangeValueForKey:@"children"];
}
}
重复的项目可能是由重复的更改通知引起的。 didChangeValueForKey:@"children"
和 mutableArrayValueForKey
都通知观察者。这会混淆树控制器。使用 willChangeValueForKey
didChangeValueForKey
组合或使用 mutableArrayValueForKey
.
observeValueForKeyPath
可以调用多次。 NSArrayController
的 arrangedObjects
在添加或删除对象时、对内容进行排序时以及每当 NSArrayController
认为 arrangedObjects
必须更改时更改。
我在 OS X 开发中的早期步骤。
NSTreeview 绑定到一个包含各种自定义代理对象的数组 class。其中一些对象绑定到我的核心数据存储,因此我的侧边栏包含多个不同 NSManagedObject
实体的列表。由于某种原因,moc 实体是重复的,每个都列出了两次。像这样:
NSTreeController 绑定到 NSArray,sidebarItems,这里填充:
- (void)populateOutlineContents
{
// hide the outline view - don't show it as we are building the content
[self.myOutlineView setHidden:YES];
BrowserItem *dashboardItem = [BrowserItem itemWithTitle:@"Home"];
dashboardItem.nodeIcon = [NSImage imageNamed:@"home"];
BrowserItem *todayItem = [BrowserItem itemWithTitle:@"Today"];
BrowserItem *thisWeekItem = [BrowserItem itemWithTitle:@"This Week"];
BrowserItem *separatorItem = [BrowserItem itemWithTitle:nil];
// Setup Projects
self.projectsSidebarGroup = [BrowserItem itemWithTitle:PROJECTS_NAME];
self.projectsSidebarGroup.childItemTitleKeypath = @"projectName";
[self.projectsSidebarGroup bindChildItemsToArrayKeypath:@"projectsArrayController.arrangedObjects" onObject:self];
// Setup Crewmen
self.crewmenSidebarGroup = [BrowserItem itemWithTitle:CREWMEN_NAME];
self.crewmenSidebarGroup.childItemTitleKeypath = @"fullName";
[self.crewmenSidebarGroup bindChildItemsToArrayKeypath:@"crewmenArrayController.arrangedObjects" onObject:self];
self.sidebarItems = @[dashboardItem, todayItem, thisWeekItem,separatorItem, self.projectsSidebarGroup, self.crewmenSidebarGroup];
[self.myOutlineView setHidden:NO]; // we are done populating the outline view content, show it again
}
在 viewDidLoad 中,我为每个组设置了核心数据绑定和 NSArrayControllers:
[self populateOutlineContents];
NSPredicate *ActiveProjectsFilter = [NSPredicate predicateWithFormat:@"inactive == NO"];
NSSortDescriptor *sortByName = [[NSSortDescriptor alloc] initWithKey:@"projectName" ascending:YES];
self.projectsArrayController = [self controllerForEntity:@"Project" filter:ActiveProjectsFilter sortBy:@[sortByName]];
NSPredicate *activeCrewmenFilter = [NSPredicate predicateWithFormat:@"Active == YES"];
NSSortDescriptor *sortByLast = [[NSSortDescriptor alloc] initWithKey:@"nameLast" ascending:YES];
NSSortDescriptor *sortByFirts = [[NSSortDescriptor alloc] initWithKey:@"nameFirst" ascending:YES];
self.crewmenArrayController = [self controllerForEntity:@"WorkMan" filter:activeCrewmenFilter sortBy:@[sortByLast, sortByFirts]];
实际控制人设置在这里:
-(NSArrayController *)controllerForEntity:(NSString *)aEntityName filter:(NSPredicate *)filter sortBy:(NSArray *)sortDescriptors
{
NSArrayController *controller = [[NSArrayController alloc] init];
controller.managedObjectContext = self.managedObjectContext;
controller.entityName = aEntityName;
controller.automaticallyRearrangesObjects = YES;
controller.automaticallyPreparesContent = YES;
if (filter) {
controller.fetchPredicate = filter;
}
if (sortDescriptors) {
controller.sortDescriptors = sortDescriptors;
}
NSError *error;
if (![controller fetchWithRequest:nil merge:NO error:&error])
{
ALog(@"Error fetching %@", aEntityName);
}
return controller;
}
我检查了我的 NSArrayControllers 和 arrangedObjects 的状态,那里只有预期数量的项目。实际上不存在重复项,那么 OutlineView 中的重复项来自哪里?
我的代理对象,以防有帮助:
#import "BrowserItem.h"
@implementation BrowserItem
+(instancetype)itemWithTitle:(NSString *)title
{
BrowserItem *item = [[BrowserItem alloc] init];
[item setTitle:title];
return item;
}
- (instancetype)init
{
if (self = [super init])
{
self.children = [NSMutableArray array];
self.modelToItemMapping = [NSMapTable weakToStrongObjectsMapTable];
self.childItemsArrayController = [[NSArrayController alloc] init];
[self.childItemsArrayController addObserver:self forKeyPath:@"arrangedObjects" options:NSKeyValueObservingOptionPrior context:NULL];
}
return self;
}
- (void)bindChildItemsToArrayKeypath:(NSString*)keypath onObject:(id)object;
{
[self.childItemsArrayController bind:@"contentArray" toObject:object withKeyPath:keypath options:nil];
self.isGroup = YES;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:@"arrangedObjects"])
{
[self willChangeValueForKey:@"children"];
NSMutableArray *watchableChildren = [self mutableArrayValueForKey:@"children"];
[watchableChildren removeAllObjects];
for (id modelItem in self.childItemsArrayController.arrangedObjects)
{
BrowserItem *browserItem = [self.modelToItemMapping objectForKey:modelItem];
if (!browserItem)
{
browserItem = [BrowserItem itemWithTitle:[modelItem valueForKey:self.childItemTitleKeypath]];
[self.modelToItemMapping setObject:browserItem forKey:modelItem];
}
[watchableChildren addObject:browserItem];
}
[self didChangeValueForKey:@"children"];
}
}
@end
------------更新---------
所以,我从 NSMutableArrayForKey
更改为创建一个新的 NSMutableArray
并在我的所有对象都设置好后替换子数组。但是,这消除了重复列表,仍然存在冲突。我在其中放置了一些调试语句和计数器,我可以看到例程在每个组上 运行 3 或 4 次。这似乎是不必要的。我想它没有重复,因为以前的结果现在被最新的 运行 取代了。还是想解决而不是原样离开
这是修改后的观察者:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:@"arrangedObjects"])
{
[self willChangeValueForKey:@"children"];
NSMutableArray *replacementChildren = [NSMutableArray array];
for (id modelItem in self.childItemsArrayController.arrangedObjects)
{
BrowserItem *browserItem = [self.modelToItemMapping objectForKey:modelItem];
if (!browserItem)
{
browserItem = [BrowserItem itemWithTitle:[modelItem valueForKey:self.childItemTitleKeypath]];
[self.modelToItemMapping setObject:browserItem forKey:modelItem];
[self.itemToModelMapping setObject:modelItem forKey:browserItem];
}
[replacementChildren addObject:browserItem];
}
_children = replacementChildren;
[self didChangeValueForKey:@"children"];
}
}
重复的项目可能是由重复的更改通知引起的。 didChangeValueForKey:@"children"
和 mutableArrayValueForKey
都通知观察者。这会混淆树控制器。使用 willChangeValueForKey
didChangeValueForKey
组合或使用 mutableArrayValueForKey
.
observeValueForKeyPath
可以调用多次。 NSArrayController
的 arrangedObjects
在添加或删除对象时、对内容进行排序时以及每当 NSArrayController
认为 arrangedObjects
必须更改时更改。