Пользовательское свойство Objective C получает бесконечную рекурсию - PullRequest
2 голосов
/ 23 июня 2011

У меня есть класс Objective-C, который реализует узел для дерева данных.Его свойства доступны только для чтения, в то время как закрытое расширение класса (здесь не показано) реализует установщики свойств, чтобы класс менеджера мог создать дерево.

// Interface
@interface DataSet : NSObject {
    NSString        *name;
    NSString        *data;
@private
    DataSet         *parent;
    NSMutableArray  *children;
}
@property (nonatomic, readonly, copy) NSString *name;
@property (nonatomic, readonly, copy) NSString *data;

Я хочу реализовать собственный метод получения для одного из свойств, которое, если свойство имеет значение nil, будет проходить вверх по дереву, пока не найдет узел-предок, который имеет значение, отличное от nil для этого свойства.,

Моя проблема заключается в реализации метода получения без бесконечной рекурсии вызова самого метода получения.

// Implementation
@interface DataSet ()
@property (nonatomic, retain) DataSet           *parent;
@property (nonatomic, retain) NSMutableArray    *children;
@end

@implementation DataSet

@synthesize name;
// do not @synthesize data
@synthesize parent, children;

// custom getter walks up tree to find first non-nil 'data' property
- (NSString*) data {
    NSString *result = nil;
    DataSet *set = self;
    while (set != nil && result == nil) {
        result = [set data];    // <=== INFINITE RECURSION HERE
        set = set.parent;
    }
    return result;
}

Я просматривал этот и другие форумы, но не нашел примеров того, что я пытаюсь сделать здесь.У кого-нибудь есть какие-либо предложения?

Кроме того, должна ли последняя строка в получателе быть

return [result copy];

?

Ответы [ 3 ]

5 голосов
/ 23 июня 2011

Хммм, я думаю, вы хотите что-то вроде этого:

-(NSString *) data {
    // Determine result from current instance data.
    NSString *result = ....; 

    // If nothing, ask parent instance of this instance.
    if (result == nil) {
        result = [parent data];
    }

    // Might still be nil if parent returns nothing.
    return result;
}

Хммм, на самом деле, если у вас есть переменная data, содержащая некоторые текстурные данные, это можно сделать так:

-(NSString *) data {
    // If data is nil, ask parent instance for a value, otherwise return a copy.
    return data == nil ? [parent data] : [data copy];
}

Таким образом, каждый экземпляр DataSet не должен иметь цикл.Все, что они делают, это проверяют с их непосредственным родителем.Таким образом, если у вас есть граф данных A -> B -> C -> D и вы выполняете [D data];, D проверит себя, а затем как C, который проверит себя, а затем как B, который проверит себя и затем спроситA. Вы вернете первое успешное не нулевое значение для результата.

3 голосов
/ 23 июня 2011

Просто получите доступ к ivar напрямую:

// custom getter walks up tree to find first non-nil 'data' property
- (NSString*) data {
    NSString *result = nil;
    DataSet *set = self;
    while (set != nil && result == nil) {
        result = set->data;
        set = set->parent;
    }
    return [result copy];
}

Это позволяет избежать вызова метода доступа к свойству и, следовательно, избежать рекурсии.

И да, последняя строка должна быть return [result copy];, потому что вы объявилиВаша собственность как имеющая собственность copy.Если бы вы не объявили это с помощью свойства copy, вы бы не вернули копию.

0 голосов
/ 23 июня 2011

Решил это.

Я могу рекурсивно вызывать пользовательский метод получения для родителя объекта (но не для самого объекта).Это не проблема.

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

// custom getter - if data is nil on this object, 
// find the first non-nil value in its list of ancestors
- (NSString*) data {
    NSString *result = data;
    if (result == nil && parent != nil)
        result = [parent data];
    return result;
}

- (void) dealloc {
    NSInteger count = [children count];
    for (NSInteger index = count - 1; index >= 0; --index) {
        DataSet *child = [children objectAtIndex:index];
        [children removeLastObject];
        [child release];
    }
    [children release];

    // DON'T call custom getter if data is nil
    // or we'll get one of its ancestors' data object, release it,
    // then later release it again when releasing the ancestor
    if (data != nil)
        [self.data release];

    [self.name release];

    [super dealloc];
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...