Программирование на 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 возвращаемый объект указывает
Но почему изменился первый элемент созданной копии? По умолчанию копии ивляклси поверхностными (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 = [И сору];