Управление памятью при обновлении элементов в NSMenu

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

Мое приложение имеет метод, который обновляет NSMenuпредметы:

//Remove and Release old Status Scan Menu:
if ([statusMenuScansMenu numberOfItems] !=0) {
    for (NSMenuItem *menueItemToBeReleased in [statusMenuScansMenu itemArray]) {
        [statusMenuScansMenu removeItem:menueItemToBeReleased];
        [menueItemToBeReleased release];
    }
}

//New Status Scan Menu:
for (MyObject* myObject in myArray) {
    NSMenuItem * scanMenuItem = [[NSMenuItem alloc] init];
    [scanMenuItem setTitle:[myObject name]];
    [statusMenuScansMenu addItem:scanMenuItem];
}

Как видите, перед добавлением новых элементов я удаляю все предыдущие элементы и отправляю release им. Затем я добавляю новые.

Это лучший способ для управления памятью?

Если я анализирую свой код в Xcode 4.1, он говорит, что существует потенциальная утечка памяти.

1 ответ

Решение

Похоже, что вы делаете это, вероятно, должно работать хорошо, но это своего рода странный способ сделать это.

Если вам может потребоваться OS X 10.6+, ваш код может быть сведен к следующему:

//Remove old Status Scan Menu:
[statusMenuScansMenu removeAllItems];

//New Status Scan Menu:
for (MyObject* myObject in myArray) {
    NSMenuItem * scanMenuItem = [[[NSMenuItem alloc] 
      initWithTitle:[myObject name] action:NULL keyEquivalent:@""] autorelease];
    [statusMenuScansMenu addItem:scanMenuItem];
}

Обратите внимание, что добавив autorelease при создании NSMenuItem в нижней петле, нет необходимости отправлять лишние release во время удаления пункта меню, как в вашем коде. В каком-то смысле NSMenu действует как NSArray делает с подменю и пунктами меню, которые это содержит: это сохраняет их. Так как вы вставляете недавно созданный NSMenuItem прямо в NSMenuэто как будто NSMenu берет на себя ответственность за пункт меню. Таким образом, вам нужно противодействовать счету удержания +1, который вы получаете при создании alloc/init элемента, чтобы убедиться, что вы не получите утечку памяти. В вашем коде вы противодействовали тому, что +1 сохраняет счет, посылая ему явный / дополнительный релиз во время удаления пункта меню, что является своего рода обходным путем. В приведенном выше коде, который я разместил, добавив autorelease во время создания в нижнем цикле единственное, что "держится" за пункты меню - это меню. Затем, позже, когда вы звоните removeAllItems метод, меню отправит release для каждого пункта меню, в этот момент их счетчик хранения должен упасть до 0, и они будут освобождены.

Если вам необходимо поддерживать версии OS X до 10.6, вы можете использовать приведенный выше код, за исключением замены [statusMenuScansMenu md_removeAllItems] за [statusMenuScansMenu removeAllItems], Вы можете создать это md_removeAllItems метод в категории на NSMenu вот так:

@interface NSMenu (MDAdditions)
- (void)md_removeAllItems;
@end


@implementation NSMenu (MDAdditions)
- (void)md_removeAllItems {
    NSUInteger currentCount = [self numberOfItems];
    for (NSUInteger i = 0; i < currentCount; i++) {
        [self removeItemAtIndex:0];
    }
}
@end
Другие вопросы по тегам