среда, 12 мая 2010 г.

Утечки памяти в дополнительных потоках (в связи с Obj-C объектами) в iPhone-приложениях

Я изучил C++ значительно лучше, поэтому признаюсь (грешен), что во многих случаях стараюсь писать код iPhone-приложений, используя при любой возможности C++ классы (и стандартные библиотеки языка).

Но в ряде случаев (особенно при работе с GUI) самый простой или даже единственный способ получить желаемый результат - использовать Objective C классы (особенно классы из Cocoa Touch). Например, когда нужен вывод изображения на весь экран или рисование иконок в ячейках TableView.

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

Оказывается, всё дело в том, что основной поток изначально окружён 2-мя волшебными вызовами функций (см. функцию main):

int main(int argc, char *argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; // первый волшебный вызов
int retVal = UIApplicationMain(argc, argv, nil, nil);
[pool release]; // второй волшебный вызов
return retVal;
}


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


- (void)mainRoutine {
// Все autorelease-объекты, которые попадают в Autorelease пул
// получат автоматически сообщение release, как
// только они перестанут быть нужны:
NSAutoreleasePool *pool =
[[NSAutoreleasePool alloc] init];

NSRunLoop* runLoop = [NSRunLoop currentRunLoop];

while (isThreadShouldBeRunning_)
{
[runLoop runMode:NSDefaultRunLoopMode
beforeDate:[NSDate distantFuture]];
}

[pool release];
}


Сегодня я узнал приятную вещь. Иногда бывает неудобно париться насчёт такой специальной функции. Оказывается, можно окружить этими 2-мя волшебными вызовами любую функцию, если только вы точно знаете, что она всегда будет вызываться в дополнительном потоке (и никогда в основном потоке). После этого утечки памяти прекратятся! Ура!

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; // первый волшебный вызов сделайте в самом начале функции
[pool release]; // второй волшебный вызов сделайте в самом конце функции

Комментариев нет:

Отправить комментарий

Постоянные читатели

Архив блога