2010-02-06 7 views
1

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

У моего приложения iPhone есть класс, основная роль которого заключается в том, чтобы инкапсулировать способ обработки данных на кучу необработанных байтов, поступающих с веб-сервера. Каждый раз, когда мне нужно отображать определенный тип информации из этих данных (например, совет, содержащийся в этих необработанных байтах), я вызываю метод getAdviceFromGame. Этот метод создает отображаемую NSString, вызывая метод класса NSString в конце (объект, отправленный обратно методом класса stringWithUTF8String, автореализован, в соответствии с правилами именования метода - нет init, нет выделения в имени). Обратите внимание, что я не помещал «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]; 

}

Этот метод, если с моей точки зрения, не имеет решающего значения, с точки зрения управления памятью зрения, так как я только отправить обратно «autoreleased» объект , Примечание: pGame - это внутренняя переменная класса типа NSData.

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

Во время выполнения кода, когда приложение запущено, я могу видеть с помощью инструмента измерения распределения, что размер выделенного объекта увеличивается (от 400 К до 800 КБ). Затем, когда метод 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. Ну, я не удовлетворен тем, как это сделать, но, возможно, это правильный способ сделать это.

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

Я боюсь, что пул авторефератов будет очищен до достижения строки. Стойка: cardSymbol.

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

ответ

0

Q1: Можем ли мы точно знать, когда autorelease бассейн будет "сливают" или "продувают"?

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

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

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

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

Обратите внимание: я не указал «Создать» в имени метода, потому что вызывающий объект не принадлежит объекту, отправленному методом.

Очень хорошо! Но я также рекомендую вам изменить имя метода от getAdviceFromGame:ofLine до adviceFromGame:ofLine. Обычно в Cocoa 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.

+0

@Yuji: Спасибо за советы по поводу наименования метода («получить» или «получить», я не знал разницы). После публикации вопроса, я получил ту же идею, чтобы выпустить cardSymbol внутри метода, используя альтернативный указатель NSString, чьи данные, указанные им, могут быть освобождены до «возврата». NSString * s = [SString stringWithString: cardSymbol]; [cardSymbol release]; return s; карточка возвратаSymbol; : Действительно, я не знал, что могу вернуть подкласс возвращаемого типа. – user255607

+0

Спасибо за ваш ответ. Я заметил, что в последнем примере я забыл «autorelease», который я исправил сейчас. Глупый я. – Yuji

0

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

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

В бассейнах с авторефератом нет магии. documentation is quite clear о том, как они работают.

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

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

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

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

+0

@bbum: Спасибо за объяснение и совет. Извините за недостаток точности в заголовке вопроса. Фактически, название является лишь частью вопроса. В самом деле, мой метод использует метод класса (в конце, при возврате): return [NSString stringWithUTF8String: currentCString]; Я сосредоточился на «методе класса», поскольку этот метод является методом класса, который автоматически регенерирует объект, который он отправляет обратно. Я постараюсь быть более точным в своем следующем заголовке вопросов. – user255607

+0

Не беспокойтесь - теперь я понимаю, почему ваш акцент был таким, каким он был. – bbum

Смежные вопросы