Правильный способ объявления, выделения, загрузки и освобождения NSMutableArray - PullRequest
6 голосов
/ 22 февраля 2010

Я объявляю свой массив в моем * .h файле:

@interface aViewController: UIViewController
{
   NSMutableArray *anArray;    // You will need to later change this many times.
}
@end

Я выделяю для этого память своим * .m файлом:

-(void) viewDidLoad
{
   anArray = [[NSMutableArray alloc] init];
}

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

   anArray = [NSMutableArray arrayWithObjects:@"one", @"two", @"three", nil];

И я освобождаю его здесь:

-(void) dealloc
{
   [anArray release];
   [super dealloc];
}

Все это выглядит нормально?

Потому что происходит сбой при последующем запуске этого кода:

   NSLog(@"%d", [anArray count]);

Не уверен, почему простой "NSLog () и count" может привести к краху всего.


Dirk

Позвольте мне выразиться так: у меня ОГРОМНОЕ неправильное понимание указателей, массивов, строк и памяти.

Я прочитал все, что могу найти на нем ... но (пока), чтобы найти простое, ясное и понятное описание.

Можете ли вы предложить один? (Надеюсь, менее 10 страниц чтения.) Есть ли ссылка, которая объясняет JUST эту тему ... и с точки зрения "у вас есть 12-летний опыт программирования ... но НИЧЕГО, что когда-либо касалось выделения памяти или указателей".)

То есть имя переменной НЕ как я ссылаюсь на объект? Тогда зачем это?

Я привык ко многим другим языкам, которые просто делают это:

myString = "this"
myString = "that"

myInt = 5
myInt = 15

(Что может быть проще.)


Похоже, это был бы самый простой способ сделать это. (И это похоже на работу. Но это действительно правильно?)

Don't alloc any memory for my NSMutableArray initially.
Don't re-alloc any memory repeatedly.  (When I change my array's values.)
Don't release it repeatedly. (Before I change my array's values.)
Don't release it when I exit the program.

But:
Always remember to use RETAIN when I initially assign 
(and repeatedly reassign) new values to my anArray variable.

Вы не загружаете свой массив anArray = [NSMutableArray arrayWithObjects: @ "один", @ "два", @ "три", ноль]; Вместо этого вы заменяете его новым экземпляром, а еще хуже - экземпляром, чей ссылка фактически принадлежит какой-то организации, которой вы не управляете

Wow. Таким образом, у меня может быть 20 массивов ... все они называются одинаково: anArray ... и все они будут разными? (Нет такого понятия, как массив GLOBAL?)

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

Итак ... сначала я должен "удалить все объекты" ... а затем я могу вызвать метод ONE , чтобы заново добавить все мои новые значения.

anArray = [[NSMutableArray arrayWithObjects: @ "one", @ "two", @ "three", nil] retain]; вместо последовательности alloc / init.

Ничего себе. Я думал, что ничто не может быть сохранено в массиве без выделения места для него.

Если вы действительно намереваетесь заменить весь массив, вы можете рассмотреть используя свойства

Как бы я это сделал, используя свойства?
Как правильно сделать что-то вроде этого:

> anArray = [NSMutableArray arrayWithObjects:@"one", @"two", @"three", nil];
> anArray = [NSMutableArray arrayWithObjects:@"four", @"five", @"six", nil];

Точно так же, как я бы сделал:

x = 12;
x = 24;

Ничего себе. Я ДЕЙСТВИТЕЛЬНО совершенно неправильно понял все, что касается строк, массивов и памяти. Я подумал, что «легкий путь» - выделить ОДИН РАЗ ... использовать изменяемый массив ... изменить его так, как вы хотите ... и освободить ОДИН РАЗ.

Проблема в том, что этот новый массив не сохраняется,

Я бы подумал, что старый массив исчезнет ... и можно будет использовать новый массив. (Но, наверное, нет.)

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

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

но нужно использовать [anArray release];

Я думал, что это заставит меня освободить память, которую я выделил ... (но я думаю, нет) ... и тогда мне придется перераспределить больше памяти. (Но я думаю, что нет.)

anArray = [[NSMutableArray arrayWithObjects: @ "один", @ "два", @ "три", ноль] сохранить];

Так что я должен "сохранить" это ... чтобы оно не исчезло из-под меня? (Не уверен, почему это так. Пока я не скажу это ... в моем последнем вызове в dealloc.)

Другой, возможно, более правильный способ исправить это - использовать addObject: или addObjectsFromArray: методы NSMutableArray вместо постоянного создания новых массивов.

Я только хочу создать массив ONE ... и использовать его так, как я хочу. Я никогда не хочу ADD к массиву. Я хочу установить новые значения.

Ответы [ 3 ]

11 голосов
/ 22 февраля 2010

Вы не загружаете свой массив с

anArray = [NSMutableArray arrayWithObjects:@"one", @"two", @"three", nil];

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

[[NSMutableArray alloc] init]

потеряно и будет слито.

Вместо того, чтобы заменять всю ссылку на массив, измените уже имеющуюся в вашем распоряжении, используя, например, addObject: как

[anArray addObject: @"one"]
[anArray addObject: @"two"]

и т.д.. Для очистки старых значений может пригодиться метод removeAllObject. Существуют также методы мутации, которые можно использовать для добавления нескольких значений одновременно.

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

anArray = [[NSMutableArray arrayWithObjects:@"one", @"two", @"three", nil] retain];

вместо последовательности alloc / init.

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

4 голосов
/ 22 февраля 2010

Проблема anArray = [NSMutableArray arrayWithObjects:@"one", @"two", @"three", nil]; Это заменяет массив, который вы изначально создали. Проблема в том, что этот новый массив не сохраняется, поэтому вы теряете его, как только метод возвращается. Кроме того, у вас есть утечка памяти, потому что вы никогда не освобождали исходный массив.

Есть несколько способов исправить это, но один из них - использовать [anArray release]; anArray = [[NSMutableArray arrayWithObjects:@"one", @"two", @"three", nil] retain];.

Другой, возможно, более правильный способ исправить это - использовать методы addObject: или addObjectsFromArray: NSMutableArray вместо постоянного создания новых массивов.

3 голосов
/ 15 мая 2011

Запомните это простое правило памяти: освобождает только принадлежащие вам объекты . Вы владеете объектами только тогда, когда создаете их, используя:

  • init (этот метод создает новый объект с счетом сохранения 1)
  • new (этот метод аналогичен использованию alloc и init)
  • copy (этот метод создает новый объект с счетом сохранения 1 и содержимым получателя метода)
  • retain (этот метод увеличивает счет сохранения с 1)

Система dealloc автоматически съедает объекты с нулевым счетом сохранения. Вы должны release каждый свой объект после того, как вы закончите с ним. Если вы release слишком рано, вы попадаете в опасную ситуацию. Если вы не release ваш объект, когда вы закончите с ним, вы получите утечку.

Указатель - это специальный объект, который ссылается на некоторый объект в памяти. Это в основном адрес памяти (и некоторые другие данные). Если вы хотите использовать объект, вы должны alloc съесть для него память, а затем init ialize. Вы назначаете (используя знак =) указатель.

string = [[NSString alloc] init];

string теперь имеет счет удержания один. Но поскольку NSString является неизменным объектом, его нельзя изменить после инициализации. Один из способов присвоить string значение - это выполнить его при инициализации.

string = [[NSString alloc] initWithString: @"Hello, World!"];

Если вам нужно регулярно менять string, вы можете использовать другой класс, который является изменяемым: NSMutableString. Но, тем не менее, единственный способ изменить их - это сообщение.

string = [[NSMutableString alloc] initWithString: @"Initial string"];
[string setString: @"Modified string"];

Отметим, что следующий код неверен и приводит к утечке памяти.

string = [[NSMutableString alloc] initWithString: @"Initial string"];
string = @"Modified string";

В первой строке string назначается вновь созданному объекту. Во втором * string присваивается другой строке. Вы теряете ссылку на вновь созданный объект и получаете утечку: вы не можете освободить объект, на который у вас нет ссылки.

У вас не возникает проблем, когда вы делаете это с целыми числами (тип int), потому что это «нативный» объект. Среда выполнения Objective C связывает эти типы данных непосредственно с их значением, а не с указателем.

int1 = 4;
int1 = 5;

Примечание 1. Не забывайте всегда указывать среде выполнения, какой тип указателя. Если вы хотите использовать string, вы должны сначала определить его в своем заголовке (если он публичный) или в реализации (если вы хотите, чтобы он был скрыт от других методов). Тогда вы можете свободно использовать имя.

NSString *string;

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

int int1;

Примечание 2. Массивы (и словари и т. Д.) Ведут себя как NSString в том смысле, что они имеют как неизменяемые, так и изменяемые варианты.

Примечание 3. Большинство классов имеют специальные методы, которые выделяют, инициализируют и автоматически выпускают все сразу.

NSArray *myArray = [NSArray arrayWithObjects: @"Butter", @"Milk", @"Honey", nil];

Автоматически освобожденный объект не нуждается в ручном release, так как система через некоторое время release сделает это за вас. Это очень удобно в ситуациях, когда вам нужен объект только на время действия метода или между методами, а не как постоянная часть объекта. Когда вы autorelease какой-то объект в основном потоке (в приложении с графическим интерфейсом), это будет release d, когда цикл обработки событий завершает текущий цикл (он завершается, когда обрабатывается какое-то событие). Подробнее об пулах автоматического выпуска можно найти в документации Apple.

Надеюсь, я помогу всем, кто плохо понимает, что такое указатели. У меня были проблемы в течение нескольких месяцев, пока я много не экспериментировал. : -)

...