Управление памятью при обновлении элементов в NSMenu - PullRequest
1 голос
/ 08 октября 2011

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

В моем приложении есть метод, который обновляет элементы 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 Ответ

5 голосов
/ 08 октября 2011

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

Если вам может потребоваться 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
...