Метод класса: это правильный способ избежать утечки памяти? - PullRequest
1 голос
/ 06 февраля 2010

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

В моем приложении для iPhone есть класс, главная роль которого заключается в том, чтобы инкапсулировать способ обработки данных в паре необработанных байтов, поступающих с веб-сервера. Каждый раз, когда мне нужно отобразить определенный тип информации из этих данных (например, совет, содержащийся в этих необработанных байтах), я вызываю метод getAdviceFromGame. Этот метод создает отображаемую NSString путем вызова метода класса NSString в конце (объект, отправленный обратно методом класса stringWithUTF8String, автоматически высвобождается в соответствии с правилами именования методов - без инициализации, без выделения в имени). Обратите внимание, что я не добавил "New" в имя моего метода, потому что вызывающая сторона не является владельцем объекта, отправленного методом.

Вот метод:

-(NSString*) getAdviceFromGame:(NSInteger)number
                    ofLine:(NSInteger)line {
// Returns the advice at line specified as argument.
NSInteger currentOffset;
NSRange currentRange;
NSInteger offsetAdvice;
NSInteger length;
char currentCString[100];

if (line == 1)
    offsetAdvice = OFF_ADVICE1;
else
    offsetAdvice = OFF_ADVICE2;

// Length is the same whateve is the line.
length = LEN_ADVICE1;

// Point to the begnning of the requested game.
currentOffset = OFF_G1_SET + (number - 1) * LEN_SET;
// Point to the selected advice.
currentOffset = currentOffset + offsetAdvice;
// Skip TL
currentOffset = currentOffset + 2;

currentRange.location = currentOffset;
currentRange.length = length;

NSLog(@"Avant getBytes");

// Get raw bytes from pGame.
// Contains a C termination byte.
[pGame getBytes:currentCString range:currentRange];

// Turn these raw bytes into an NSString.
// We return an autoreleased string.
return [NSString stringWithUTF8String:currentCString];

}

Этот метод, если с моей точки зрения, не критичен с точки зрения управления памятью, поскольку я отправляю обратно только "автоматически освобожденный" объект. Примечание: pGame является внутренней переменной класса типа NSData.

В моем приложении, чтобы понять, как ведут себя автоматически выпущенные объекты, я зациклил 10000 раз этот метод в функции приложения - (void) applicationDidFinishLaunching: (UIApplication *), а также 10000 раз тем же методом в - (void) tabBarController : (UITabBarController *) tabBarController didSelectViewController: (UIViewController *) viewController. Таким образом, я могу использовать большие ассигнования.

Во время выполнения кода, когда приложение запускается, я вижу с помощью инструмента измерения alloc, что размер выделенного объекта увеличивается (с 400K до 800K). Затем, когда метод applicationDidFinishLaunching заканчивается, сумма уменьшается до 400K. Таким образом, я предполагаю, что пул был «истощен» операционной системой (своего рода управление мусором).

Когда я нажимаю на панель вкладок, снова происходит большое выделение из-за цикла. Кроме того, я вижу рост размера (потому что тысячи NSString выделены и отправлены обратно). Когда это будет сделано, размер уменьшится до 400K.

Итак, мои первые вопросы: Q1: Можем ли мы точно знать, когда пул авто-выпусков будет «слит» или «очищен»? Q2: это происходит в конце методов OS / GUI, таких как didSelectViewController? Q3: Должен ли кто-то, кто вызывает getAdviceFromGame, сохранить объект, отправленный обратно моим методом, перед его использованием?

Теперь у меня есть другой (более сложный) метод, в котором я внутренне выделяю изменчивую строку, над которой я работаю, перед отправкой обратно строки NSString:

-(NSString*) getBiddingArrayFromGame:(NSInteger)number
                           ofRow:(NSInteger)row
                          ofLine:(NSInteger)line {
NSInteger offset;
char readByte;
NSMutableString *cardSymbol = [[NSMutableString alloc] initWithString:@""];
NSRange range;

// Point to the begnning of the requested game.
offset = OFF_G1_SET + (number - 1) * LEN_SET;

// Returns the array value from cell (row, line)
// We must compute the offset of the line.
// We suppose that the offset cannot be computed, but
// only deduced from line number through a table.
switch (line) {
    case 1:
        offset = offset + OFF_LINE1;
        break;
    case 2:
        offset = offset + OFF_LINE2;
        break;
    case 3:
        offset = offset + OFF_LINE3;
        break;
    case 4:
        offset = offset + OFF_LINE4;
        break;
    default:
        // This case should not happen but for robustness
        // we associate any extra value with a valid offset.
        offset = OFF_LINE4;
        break;
}

// Skip TL bytes
offset = offset + 2;

// From the offset and from the row value, we can deduce
// the offset in the selected line.
offset = offset + (row - 1);

// Now, we must read the byte and build a string from
// the byte value.
range.location = offset;
range.length = 1;
[pGame getBytes:&readByte range:range];

// We must extract the family type.
// If the family if of type "Special" then we must build by
// hand the value to display. Else, we must build a string
// with the colour symbol and associated character by reading
// in the card character table.
switch (readByte & CARD_FAMILY_MASK) {
    case COLOUR_CLUBS:
        // "Trèfles" in French.
        [cardSymbol appendString:CLUBS_UTF16];
        break;
    case COLOUR_DIAMONDS:
        [cardSymbol appendString:DIAMONDS_UTF16];
        break;
    case COLOUR_HEARTS:
        [cardSymbol appendString:HEARTS_UTF16];
        break;
    case COLOUR_SPADES:
        [cardSymbol appendString:SPADES_UTF16];
        break;
    case COLOUR_SPECIAL:
        break;
    case COLOUR_ASSET:
    default:
        break;
}

[cardSymbol autorelease];

// Return the string.
return [NSString stringWithString:cardSymbol];

}

Как видите, это не очень сложно, но более критично с точки зрения управления памятью, поскольку я "внутренне" выделяю и инициирую строку NSString. Поскольку я использую его в конце метода, я могу только автоматически высвобождать его перед вызовом stringWithString: cardSymbol (на самом деле я хотел бы освободить его так, чтобы он был освобожден прямо сейчас), иначе он мог бы быть освобожден перед методом stringWithString: cardSymbol. Ну, я не удовлетворен тем, как это сделать, но, возможно, это правильный путь.

Таким образом, мой последний вопрос: это правильный способ сделать это?

Боюсь, что пул авто-релиза будет очищен до достижения stringWithString: cardSymbol.

С наилучшими пожеланиями, Franz

Ответы [ 2 ]

0 голосов
/ 06 февраля 2010

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

Таким образом, мои первые вопросы: Q1: Может мы точно знаем, когда происходит авто-релиз бассейн будет "осушен" или "очищен"? Q2: это происходит в конце Методы OS / GUI, такие как didSelectViewController? Q3: должен кто-то, кто вызывает getAdviceFromGame сохранить объект, отправленный обратно моим метод перед его использованием?

Нет волшебства с пулями авто-релиза. Документация совершенно ясна о том, как они работают.

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

Как видите, это не очень сложный, но более критичный с точка зрения управления памятью с Я «внутренне» выделяю и инициирую NSString. Так как я использую его в конце метод, я могу только автоматически выпустить его перед звонком stringWithString: cardSymbol (на самом деле я хотел бы выпустить его так, чтобы он освободили прямо сейчас) иначе могло бы быть освобожденным до stringWithString: метод cardSymbol. Ну, я не удовлетворен тем, как сделать это, но, возможно, это правильно способ сделать это.

Если вы явно не создадите и не истощите пул автоматического выпуска, ваша строка не исчезнет из-под вас. В сливе бассейнов нет ничего автоматического.

Нет причин создавать неизменяемую копию изменяемой строки. Просто верните изменяемую строку. Если вы действительно действительно хотите создать неизменяемую копию, сделайте так, как рекомендовал Юджи.

0 голосов
/ 06 февраля 2010

Q1: Можем ли мы точно знать, когда пул авто-выпусков будет «осушен» или «очищен»?

Q2: это происходит в конце методов OS / GUI, таких как didSelectViewController?

Да, пул автообновления сливается один раз за цикл обработки событий.

В3: Должен ли кто-то, кто вызывает getAdviceFromGame, сохранить объект, отправленный обратно моим методом, прежде чем использовать его?

Если этот человек хочет сохранить объект после этого конкретного цикла события, да.Если нет, вам не нужно этого делать, потому что объект autorelease d гарантированно останется живым до тех пор, пока не вернется ваш текущий метод обработки событий.

Обратите внимание, что я не поставил "New"в моем имени метода, потому что вызывающая сторона не владеет объектом, отправленным обратно методом.

Очень хорошо!Но я также рекомендую вам изменить имя метода с getAdviceFromGame:ofLine на adviceFromGame:ofLine.Обычно в Какао get... используется, когда что-то возвращается указателем, переданным в качестве аргумента метода, именно тогда, когда вы использовали [pGame getBytes:&readByte range:range];.

Что касается второй части, вы можете использовать вместо своих строк

[cardSymbol autorelease];

// Return the string.
return [NSString stringWithString:cardSymbol];

последовательность

NSString*s=[SString stringWithString:cardSymbol];
[cardSymbol release];
return s;

или даже просто

return [cardSymbol autorelease];

, потому что NSMutableString является подклассом NSString.Но только один объект в пуле автоматического выпуска не будет иметь большого значения, даже на iPhone.

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