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

ЖАНРЫ

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

на 12 = 11;

и удалите высвобождение (release) 12 в конце программы, после чего увидите другие результаты.

Если ваш класс может быть подклассом, то метод copyWithZone: будет насле-доваться. В таком случае вы должны изменить строку этого метода Fraction *newFract = [[Fraction allocWithZone: zone] init]; на строку Fraction *newFract = [[[self class] allocWithZone: zone] init];

Это позволяет выделить память для нового объекта изданного класса, кото-рый является получателем копии. (Например, если это подкласс с именем NewFraction, то нужно выделить в наследуемом методе память для нового объекта NewFraction вместо

объекта Fraction.)

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

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

Каждый раз, реализуя метод-установщик (setter) или метод-получатель (getter), вы должны продумать, что будет сохраняться в переменных экземпляра, что будет считываться и нужно ли защитить эти значения. Рассмотрим оператор, в котором мы задаем имя одного из объектов AddressCard с помощью метода setName: [newCard setName: newName];

Предположим, что newName — это строковый объект, содержащий имя новой карточки. Предположим также, что внутри процедуры установщика мы просто присваиваем параметр соответствующей переменной экземпляра. -(void) setName: (NSString *) theName { name = theName; }

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

Чтобы исключить этот побочный эффект, следует создать копию объекта в процедуре установщика. С помощью метода alloc мы создали новый строковый объект и затем с помощью метода initWithString: присвоили ему значение пара-метра, передаваемого методу.

Можно также написать версию метода setName:, чтобы использовать сору, например -(void) setName: (NSString *) theName { name = [theName copy];}

Чтобы управлять памятью в процедуре установщика было удобно, необхо-димо сначала автоматически высвободить (autorelease) старое значение, как показано ниже. -(void) setName: (NSString *) theName { [name autorelease]; name = [theName copy]; }

Если задать атрибут copy в объявлении свойств (property) для переменной экземпляра, то в синтезируемом методе будет использоваться метод класса сору (написанный вами или унаследованный). Поэтому объявление @property (nonatomic, copy) NSString *name;

приведет к созданию синтезируемого метода, который действует следующим образом. -(void) setName: (NSString *) theName { if (theName != name) { [name release] name = [theName copy]; } }

Атрибут nonatomic указывает системе, что в данном случае не нужно защи-щать методы доступа с помощью блокировки mutex (mutually exclusive — взаимоисключение). При написании кода с защитой потоков используются блокировки mutex, чтобы исключить одновременное выполнение одного кода двумя потоками (ситуация, которая часто вызывает ужасные проблемы). Но эти блокировки могут замедлять выполнение программ, поэтому вы можете отказаться от них, если знаете, что этот код будет всегда выполняться

только в одном потоке.

Если атрибут nonatomic не указан или вместо него указан атрибут atomic (это атрибут по умолчанию), то переменная экземпляра будет защищена блокировкой mutex. Кроме того, синтезируемый метод-получатель будет удерживать (retain) и автоматически высвобождать (autorelease) переменную экземпляра перед тем, как будет возвращено ее значение. В среде без сборки мусора это защищает переменную экземпляра от возможной перезаписи методом-установщиком, который высвобождает старое значение переменной экземпляра, прежде чем установить новое значение. Использование retain в методе-получателе гарантирует, чтобы память для старого значения не будет освобождена.

Примечание. Проблема высвобождения и автоматического высвобождения (retain/autorelease) не актуальна в среде со сборкой мусора, в которой вызовы этих методов игнорируются, но это не относится к блокировке mutex. Если ваш код будет выполняться в многопотоковой среде, предусмотрите использование методов доступа с атрибутом atomic,.

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

Вернемся к реализации метода сору. Если мы копируем переменные экземп-ляра, которые содержатнемутабельные объекты (например, немутабельные строковые объекты), то создание новой копии содержимого объекта, возможно, не потребуется. Достаточно создать новую ссылку на объект путем его удержания (retain). Например, если мы реализуем метод сору для класса AddressCard с члена-ми name и email, то достаточно написать следующую реализацию для copyWithZone:. -(AddresssCard *) copyWithZone: (NSZone *) zone { AddressCard *newCard = [[AddressCard allocWiihZone: zone] init]; [newCard retainName: name andEmail: email]; return newCard; -(void) retainName: (NSString *) theName andEmail: (NSString *) IheEmail { name = [theName retain]; email = [theEmail retain]; }

Здесь для копирования переменных экземпляра не используется метод setName:andEmail:, поскольку он создает новые копии своих аргументов. Вместо этого мы просто удерживаем (retain) две переменные с помощью нового метода retainName:andEmail:. (Мы могли бы задавать эти две переменные экземпляра в newCard непосредственно внутри метода copyWithZone:, но для этого нужны операции с указателями, без которых мы обходились до настоящего момента. Операции с указателями были бы более эффективны и не показывали бы пользователю этого класса метод [retainNameiandEmail;], не предназначенный для общего пользования, поэтому в какой-то момент вам, возможно, придется этому научиться — но не сейчас!)

В данном случае достаточно использовать удержание (retain) для переменных экземпляра вместо создания их полных копий, поскольку владелец скопированной карточки не может повлиять на компоненты пате и email исходной карточки. Убедитесь сами, что это действительно так (подсказка: он должен работать с методами-установщиками). Упражнения

Реализуйте метод сору для класса AddressBook согласно протоколу NSCopying. Имеет ли смысл реализовать также метод mutableCopy? Почему?

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