Вложенная структура NSDictionary - Как хранить ссылку на родителя каждого узла в дереве - PullRequest
0 голосов
/ 30 декабря 2011

Используя NSJSONSerialization для возвращенных NSData из сетевого вызова, я получаю обратно вложенную структуру NSDictionaries и NSArrays.

Теперь я хотел проанализировать эту древовидную структуру и подготовить ее для дальнейшего использования.Каждый узел дерева всегда несет NSArray подузлов (NSDictionaries).Каждый из этих узлов должен иметь обратную ссылку на свой родительский узел, содержащий NSArray, частью которого является подузел.

Это базовый пример структуры, о которой я говорю:

Node {
 nodes:[
  node {parent:Node,name:foo},
  node {parent:Node,name:bar},
  node {parent:Node,name:baz},
 ]
,name:root}

Каждый узел является NSDictionary, а каждый подузел - коллекцией NSArray, содержащей NSDictionaries.

Я узнал, что не могу просто добавить новый ключ "parent" и установить его значение в словаре родительского узла.Это создает segfault при вызове объекта.

Базовый пример кода, создание родительского ключа:

NSMutableDictionary * foo = [NSMutableDictionary dictionaryWithObjectsAndKeys:@"foo",@"name",[NSNumber numberWithInt:1],@"value",nil];
NSMutableDictionary * bar = [NSMutableDictionary dictionaryWithObjectsAndKeys:@"bar",@"name",[NSNumber numberWithInt:2],@"value",nil];
NSMutableDictionary * baz = [NSMutableDictionary dictionaryWithObjectsAndKeys:@"baz",@"name",[NSNumber numberWithInt:3],@"value",nil];

NSMutableArray *array = [NSMutableArray arrayWithObjects:foo,bar,baz,nil];

NSMutableDictionary * container = [NSMutableDictionary dictionaryWithObjectsAndKeys:@"root",@"name",array,@"nodes",nil];

[foo setValue:container forKey:@"parent"];

NSLog(@"%@",foo);  // <-- segfault here

Почему я получаю ошибку сегментации?Является ли это бесконечным циклом при выводе описания структуры из-за обратной ссылки в родительском ключе узла?

У вас, ребята, есть какой-то другой подход к этой проблеме здесь?Должен ли я содержать внешнее представление древовидной структуры, указывающее на каждый ключ или IS , на самом деле существует способ хранения какой-либо ссылки на родительский узел ??

Много, многозаранее спасибо !!!

Ответы [ 3 ]

2 голосов
/ 30 декабря 2011

Segfault на линии NSLog происходит из-за бесконечного цикла.Вы можете сломать это, создав подкласс NSMutableDictionary и переопределив -description, чтобы специально исключить печать значения для ключа "parent".В более общем смысле NSMutableDictionary не предназначен для включения своего собственного контейнера.Во-первых, NSDictionary сохраняет свои дочерние объекты, поэтому контейнер сохраняет foo, а foo сохраняет контейнер, что создает цикл сохранения.

Мой подход заключается в написании собственных классов моделей.Вы можете использовать объект NSMutableDictionary для хранения дочерних узлов со слабым @ property / ivar, содержащим ссылку на родительский узел.

1 голос
/ 30 декабря 2011

Мне кажется, что вы можете использовать здесь простой целевой класс C с интерфейсом, подобным

@interface Node : NSObject {
    Node              *parent;
    NSMutableArray    *nodes;
    NSString          *name
}
@end

. Я не уверен, что это лучший способ сделать это, но вы не должны использоватьNSDictionary.Причина, по которой вы получаете ошибку сегментации, вероятно, из-за обратной ссылки, создающей бесконечный цикл в NSLog.

РЕДАКТИРОВАТЬ: После поиска в Google, я обнаружил, что есть класс NSTreeNode, который должен сделать вещи прощедля вас.

http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/NSTreeNode_class/Introduction/Introduction.html

0 голосов
/ 05 января 2013

Я обошел ограничение, которое препятствует тому, чтобы вложенные NSDictionaries возвращались к содержащим NSDictionaries (циклические графы), оборачивая вложенные словари в экземпляры NSProxy:

@interface MyObjectProxy : NSProxy {
    id proxied;
}

-(id)initWithObject:(id)obj;

@end

@implementation MyObjectProxy

-(id)initWithObject:(id)obj
{
    proxied = obj;
    return self;
}

-(void)forwardInvocation:(NSInvocation*)invocation
{
    [invocation invokeWithTarget:proxied];
}

-(NSMethodSignature*)methodSignatureForSelector:(SEL)selector
{
    return [[proxied class] instanceMethodSignatureForSelector:selector];
}

-(NSString*)descriptionWithLocale:(id)locale indent:(NSUInteger)level
{
    return [[self class] description];
}

@end

Если я правильно понимаю NSProxy, этот перехват сохраняетсясообщения, которые, в свою очередь, предотвращают циклы сохранения, возникающие при непосредственном добавлении вложенных словарей, содержащих обратные ссылки.В любом случае, я знаю, что мои прокси освобождены.

Есть три незначительные проблемы с этим, которые я обнаружил.Во-первых, NSDictionary:descriptionWithLocale:indent: проходит всю свою структуру, что вызывает бесконечный цикл, уже отмеченный.Хорошая новость заключается в том, что «переопределение» его в NSProxy позволяет обойти ситуацию «залипания», обычно возникающую при попытке переопределить класс, являющийся частью кластера классов (как NSDictionary).

Вторая проблема заключается в том, что вызов MyObjectProxy:class возвращает класс MyObjectProxy, а не класс NSDictionary.В моем случае это не проблема, но об этом нужно знать.

Третья проблема заключается в том, что удаление дочернего словаря из родительского словаря и освобождение родителя приводит к тому, что соответствующий MyObjectProxy:proxied становится зомби, посколькуMyObjectProxy не может сохранить ссылку без повторного ввода цикла сохранения.Опять же, это не проблема в моем случае, так как я никогда не удаляю отдельные узлы из своей структуры, но ограничение того, что вы рассматриваете всю структуру как неизменную, является ограничением, о котором нужно знать.

...