Хранятся ли строки NSS в куче или в стеке и что является хорошим способом для инициализации - PullRequest
8 голосов
/ 11 сентября 2011

У меня есть 2 новых вопроса:

1) Рассмотрим эту строку:

NSString *myString = [[NSString alloc] initWithString: @"Value"]; 

Я узнал две вещи, но я хотел бы получить подтверждение: Как я узнал, сообщение «alloc» указывает, что экземпляр NSString будет храниться в памяти «heap». Я также понял, что примитивные переменные, такие как "символы", хранятся в памяти "стека".

Означает ли это, что:

  • экземпляр NSString хранится в памяти кучи;
  • И что этот объект имеет указатель iVar (когда был вызван метод initWithString) на строку «Value» примитивных «символов», которые находятся в памяти стека? Как это работает в действительности?

Второй вопрос имеет прямое отношение и вызывает у меня личную дилемму (вероятно, из-за того, что я упускаю точку): 2) Какой из двух подходов вы бы посоветовали и почему ?:

NSString *myString = [[NSString alloc] initWithString: @"Value"]; 
NSString *myString = @"Value"; 

Если мой первый вопрос подтвердится, оба подхода «в конце» должны указывать на символы, хранящиеся в памяти стека. Поэтому я на самом деле не вижу цели использования первого варианта и не беспокоюсь о количестве оставшихся.

Ответы [ 2 ]

13 голосов
/ 11 сентября 2011

Краткий ответ: В этом случае обе строки имеют одинаковый результат. Целесообразно присвоить строковую константу непосредственно myString.

Более длинный ответ:

Это правда, что объекты Objective C размещаются в куче.Однако это не так, что «примитивные» значения всегда хранятся в стеке.Локальные переменные хранятся в стеке, независимо от того, являются ли они примитивными или нет.(Вы можете, например, объявить локальную переменную, которая является структурой, которая не считается примитивной.) Вы можете хранить примитивные значения в куче, но единственный способ получить к ним доступ в этом случае - через указатель.Например:

int *someInt = malloc(sizeof(int));  // allocate a block of memory to hold an int
*someInt = 42;                       // store data in that memory

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

Итак, вернемся к вашим строкам.Попробуйте выполнить следующие две строки:

NSString *foo = [[NSString alloc] initWithString:@"foo"];
NSString *bar = @"foo";

Если вы прервитесь после второй строки, вы обнаружите, что foo и bar содержат один и тот же адрес;то есть они указывают на один и тот же объект.Поскольку объекты NSString являются неизменяемыми, создание объекта с константной строкой просто возвращает указатель на эту константу.

Почему в NSString даже есть -initWithString:, если это все, что он делает?NSString - это «кластер классов», то есть NSString является открытым интерфейсом к нескольким различным внутренним классам.Если вы передаете NSString *, которая не является константой, в -initWithString:, возвращаемый вами объект может быть экземпляром другого класса, чем тот, который вы получаете, когда используете константу.Как кластер классов, NSString скрывает от вас много деталей реализации, так что вы получаете хорошую производительность для различных типов строк, не беспокоясь о том, как все это работает.

5 голосов
/ 11 сентября 2011

NSString s интересны тем, что до недавнего времени они были единственным типом объекта Objective-C, который мог быть представлен как литерал.Блочные объекты, добавленные в iOS 4 и OS X 10.6, являются еще одним недавним дополнением, о котором я знаю, но у них есть свои особые правила, поэтому я упоминаю, что только для полноты. Примитивы C

могут храниться накуча или в стеке.Например:

- (void)someMethod
{
    int i = 3; // i is on the stack
}

- (void)someOtherMethod
{
    int *i = (int *)malloc(sizeof(int)); // i now points to an 'int' on the heap
}

Большинство объектов Objective C могут храниться только в куче.Вы совершенно правы, когда говорите, что alloc предоставляет новую копию объекта в куче, и результатом вашего вызова myString будет указатель на строку NSSt в куче.

Тем не менее, синтаксис @"" является сокращением для создания объекта.@"Value" фактически создает литерал объекта.Так, например, вы можете сделать:

NSLog(@"%@", [@"Value" substringFromIndex:1]);

И вывод будет 'alue'.Вы можете отправить сообщение substringFromIndex: на @"Value", потому что это буквальный объект.

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

NSMutableString *mutableString = [NSMutableString stringWithString:@"String"];
NSString *immutableString = [NSString stringWithString:mutableString];

[mutableString appendString:@" + hat"];

// immutableString would now have mutated if it was simply
// keeping a reference to the string passed in

Поэтому вам не нужно беспокоиться о сроке службы всего, что вы ему передаете.Это работа NSString, чтобы справиться с этим.

На практике, NSString литералы объекта фактически никогда не истекают, поэтому вопрос немного спорный.Однако, если бы вы использовали initWithUTF8String: или что-то еще, которое принимает литерал C, и этот литерал C был в стеке, вам все равно не о чем беспокоиться, потому что NSString с этим справится.

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

...