Чтение онлайн

ЖАНРЫ

Программирование на Objective-C 2.0
Шрифт:

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. Реализация протокола

Если применить метод сору к одному из ваших собственных классов, например, к вашей адресной книге (address book), как в строке NewBook = [myBook mutableCopy];

то будет выдано сообщение об ошибке: *** -[AddressBook copyWithZone:]: selector not recognized (селектор не распознан) *** Uncaught exception: (Невыявленное исключение) *** -[AddressBook copyWithZone:]: selector not recognized

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

Теперь покажем, как добавить метод

сору в класс Fraction, который много ис-пользовался в части I. Эти способы вполне применимы для ваших собственных классов. Если эти классы являются подклассами любого из классов Foundation, то потребуется реализация более сложной стратегии копирования, поскольку' в суперклассе может быть уже реализована его собственная стратегия копирования.

Напомним, что класс Fraction содержит две целые переменные экземпляра: numerator (числитель) и denominator (знаменатель). Чтобы создать копию одного из этих объектов, необходимо выделить пространство для новой дроби (fraction) и затем скопировать значения этих переменных в эту новую дробь.

При реализации протокола ваш класс должен реализовать метод copyWithZone:, чтобы реагировать на сообщение сору. (Сообщение сору просто передает сообщение copyWithZone: в наш класс с аргументом nil.) Если вам нужно отличать мутабельные и немутабельные копии, то потребуется также реализовать метод mutaЫеСоpyWithZone: согласно протоколу . Если вы реализуете оба метода, то copyWithZone: будет возвращать немугабельную копию, a mutableCopyWithZone: будет возвращать мутабельную копию. Создание мутабель- кой копии объекта не требует, чтобы копируемый объект был тоже мутабель- ным, и наоборот; вполне возможно, что может требоваться мутабельная копия немутабельного объекта (например, строкового объекта).

Директива @interface должна выглядеть следующим образом. @interface Fraction: NSObject <NSCopying>

Fraction — это подкласс NSObject, подчиняющийся протоколу NSCopying.

В файле секции implementation Fraction.m добавьте следующее определение для нового метода. -(id) copyWithZone: (NSZone *) zone { Fraction *newFract = [(Fraction allocWithZone: zone] init]; [newFract setTo: numerator over: denominator]; return newFract; }

Аргумент zone применяется для разных зон памяти, которые вы можете вы-делить для работы с профаммой. Это требуется только в том случае, если ваши приложения занимают много памяти, и вы хотите оптимизировать выделение памяти, группируя ее по зонам. Вы можете брать значение, передаваемое методу copyWithZone:, и передавать его методу выделения памяти allocWithZone:. Этот метод выделяет память в указанной зоне.

После выделения памяти для нового объекта класса Fraction в него копиру-ются переменные получателя numerator и denominator. Предполагается, что метод copyWithZone: будет возвращать новую копию объекта, которую вы создаете в своем методе.

Этот новый метод проверяется в профамме 18.3. // Копирование дробей #import "Fraction.h" Jfimport <Foundation/NSAutoreleasePool.h> int main (int arge, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; Fraction *11 = [[Fraction alloc] init]; Fraction *f2; [ft setTo: 2 over: 5]; f2 = [fl copy]; [!2 setTo: 1 over: 3]; [fl print]; [12 print]; [fl release]; [f2 release]; [pool drain]; return 0; }

Вывод программы 18.3 2/5 1/3

Эта программа создает объект класса Fraction с именем fl и присваивает ему значение 2/5. Затем вызывается метод сору для создания копии, который передает сообщение copyWrthZone: этому объекту. Этот метод создает новый объект класса Fraction, копирует в него значения из 11 и возвращает результат. После воз-врата в main этот результат присваивается 12. Последующее присваивание 12 дро-би 1/3 подтверждает, что это не оказывает влияния на исходную дробь 11. Изме-ните строку программы 12 = [И сору];

Поделиться с друзьями: