Шрифт:
удаляет первый элемент из массива dataArray2, но не из массива dataArray.
Это показано в программе 18.1. #import <Foundation/NSObject.h> #import <Foundation/NSArray.ti> #import <Foundation/NSString.h> ffimport <Foundation/NSAutoreleasePool.h> int main {int arge, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; NSMutableArray *dataArray = [NSMutableArray arrayWithObjects: @none'', @"two", @"three", @"four", nil]; NSMutableArray *dataArray2; // простое присваивание dataArray2 = dataArray; [dataArray2 removeObjectAtlndex: 0]; NSLog (@"dataArray:"); for ( NSString *elem in dataArray ) NSLog (@" elem); NSLog (@"dataArray2:"); for ( NSString *elem in dataArray2 ) NSLog (@n elem); // копирование, затем удаление первого элемента из копии dataArray2 = [dataArray mutableCopy]; [dataArray2 removeObjectAtlndex: 0]; NSLog {@"dataArray: for ( NSString *elem in dataArray ) NSLog (@n %@\ elem); NSLog (@"dataArray2: "); for ( NSString *elem in dataArray2 ) NSLog (@" %@", elem); [dataArray2 release]; [pool drain]; return 0; }
Вывод программы 18.1 dataArray: two three four dataArray2: two three four dataArray: two three four dataArray2: three four `` В программе определяется объект мутабельного массива dataArray, и его эле-ментам присваиваются строковые объекты @"one", @"two", @"three", @"four" соответственно. Как говорилось выше, оператор присваивания
dataArray2 = dataArray; просто создает еще одну ссылку на тот же объект массива в памяти. Поэтому после удаления первого объекта из dataArray2 и вывода элементов объектов-мас-сивов первый элемент (строка @''опе") исчезает из обеих ссылок на этот объект-массив. Затем мы создаем мутабельную копию dataArray и присваиваем полученную копию массиву dataArray2. В результате получаются два отдельных мутабельных массива в памяти, каждый из которых содержит три элемента. Теперь удаление первого элемента из dataArray2 не оказывает влияния на содержимое массива dataArray, что подтверждается последними двумя строками вывода программы. Отметим, что для создания мутабельной копии объекта копируемый объект не обязан бытьмутабельным. То же относится и кнемутабельным копиям: можно сделать немутабельную копию мутабельного объекта. При создании копии массива операция копирования автоматически наращивает счетчик ссылок (удержаний) для каждого элемента массива. Поэтому после создания копии массива и последующего высвобождения (release) исходного массива элементы копии продолжают действовать. Но поскольку копия массива dataArray была создана в этой программе с по-мощью метода mutableCopy, вы обязаны сами освободить его память. Как расска-зывалось в предыдущей главе, вы обязаны сами высвобождать объекты, которые создали с помощью одного из методов копирования, поэтому в конце программы 18.1 вставлена строка
[dataArray2 release]; ## 18.2. Поверхностное и глубокое копирование В программе 18.1 элементы массива dataArray заполняются немутабельными стро-ками (напомним, что константные строковые объекты яатяютси немутабельными). В программе 18.2 мы будем заполнять его мутабельными строками, чтобы можно было изменить одну из арок в этом массиве. Просмотрите программу 18.2 и постарайтесь понять ее вывод. import ffimport import import
int main (int arge, char argv[]) { NSAutoreleasePool pool = [[NSAutoreleasePool alloc] init]; NSMutableArray dataArray = [NSMutableArray arrayWithObjects: [NSMutableString stringWithString: @"one"], [NSMutableString stringWithString: @"two"], [NSMutableString stringWithString: @"three"], nil ]; NSMutableArray dataArray2; NSMutableString mStr; NSLog (@"dataArray:"); for ( NSString elem in dataArray ) NSLog {@" %@", elem); // создание копии, затем изменение одной из строк dataArray2 = [dataArray mutableCopy]; mStr = [dataArray objectAtlndex: 0]; [mStr appendString: @"ONE"]; NSLog (@"dataArray: for ( NSString elem in dataArray ) NSLog (@" %@", elem); NSLog (@"dataArray2: "); for ( NSString e!em in dataArray2 ) NSLog (@" %@", elem); [dataArray2 release]; [pool drain]; return 0; } Вывод программы 18.2
dataArray: one two three dataArray: oneONE two three dataArray2: oneONE two three `` Мы считываем первый элемент массива dataArray с помощью оператора mStr = [dataArray objectAtlndex: 0]; и добавляем в него строку' @"ONE" с помощью оператора [mStr appendString: @"ONE"];
Обратите внимание на значение первого элемента исходного массива и его копии: в обоих массивах оно было изменено. Понятно, почему был изменен первый элемент dataArray, но почему была изменена и его копия? Получая элемент из коллекции, мы получаем новую ссылку на этот элемент, но не новую копию. Поэтому при вызове метода objectAtlndex: для dataArray возвращаемый объект указывает на тот же объект в памяти, что и первый элемент в dataArray. Последующее изменение строкового объекта mStr также сопровождается изменением первого элемента массива dataArray, что подтверждается результатами вывода.
Но почему изменился первый элемент созданной копии? По умолчанию копии ивляклси поверхностными (shallow) копиями. Когда массив был скопирован с помощью метода mutableCopy, в памяти было выделено пространство для нового объекта-массива, и элементы были скопированы в новый массив. Но копирование каждого элемента массива из исходного места в новое означает только копирование ссылки из одного элемента массива в другой. В результате элементы обоих массивов ссылаются на одни и те же строки в памяти, что не отличается от присваивания одного объекта другому, о котором мы говорили в начале этой главы.
Чтобы создать другие копии каждого элемента массива, необходимо выпол-нить глубокое (deep) копирование, при котором создаются копии содержимого каждого объекта в массиве, а не копируются ссылки на объекты (подумайте, что это означает, если элемент массива сам является объектом-массивом). Но глубокое копирование не выполняется по умолчанию, если мы используем методы сору или mutableCopy с классами Foundation. В главе 19 мы покажем возможности архивации Foundation для создания глубокой копии объекта.
Копируя массив, словарь или набор, мы получаем новую копию этих кол-лекций. Создание копий отдельных элементов может потребоваться, например, если нужно внести изменения в коллекцию, но не в ее копию. Например, если в программе 18.2 нужно было бы изменить первый элемент массива dataAnay2, но не dataArray, вы могли бы создать новую строку (например, с помощью ме тода stringWithString:) и сохранить ее в первом элементе dataArray2 с помощью оператора mStr = [NSMutableString stringWithString: [dataArray2 objectAtlndex: 0]];
Затем можно было бы внести изменения в переменную mStr и добавить ее в этот массив с помощью метода replaceObject: at I ndex:withObject: [mStr appendString @"ONE"]; [dataArray2 replaceObjectAtlndex: 0 withObject: mStr];
Даже после замены объекта mStr и первый элемент dataArray2 ссылаются на один и тот же объект в памяти. Поэтому последующее изменение в mStr вызовет также изменение первого элемента э того массива. Чтиобы избежать этого, выс-вободите (release) mStr и выделите память (alloc) для нового экземпляра, поскольку метод replaceObject:allndex:withObject: автоматически удерживает объект. 18.3. Реализация протокола