В главе 3, я пояснял, что одной из главных причин существования экземпляров, а не просто классов, является то, что экземпляры могут иметь переменные экземпляра.

Как вы помните, переменные экземпляра объявляются при определении класса, и в главе 4, я говорил, что эти объявления располагаются в фигурных скобках в начале раздела интерфейса, или, в современной версии языка Objective-C, в разделе реализации. Но каждый отдельный экземпляр класса хранит свои переменные экземпляра, объявленные в классе, отдельно; значение любой переменной экземпляра может быть установлено и получено, пока существует сам экземпляр класса.

 

Термин “переменная экземпляра” (instance variable) встречается так часто, что , его часто сокращают до ivar.

 

Давайте напишем класс, использующий переменные экземпляров. Предположим, у нас есть класс Dog и мы хотим, чтобы каждый экземпляр класса Dog имел номер, который представляет собой число типа int. (Этот номер может соответствовать, например, номеру лицензии собаки.) В современной версии языка Objective-C мы, вероятно, объявили бы number в разделе реализации класса Dog:

@implementation Dog { int number;
}
/ / Здесь находятся реализации методов
@end

(Вы можете спросить, а почему я, например, не использую кличку собаки? Причина в том, что кличка была бы экземпляром NSString, который представляет собой объект. Переменные экземпляров, являющиеся указателями на объекты, вызывают дополнительные проблемы, которые я не хочу обсуждать в данный момент. Переменные экземпляров, представляющие собой простые типы данных С, таких проблем не вызывают. Мы вернемся к этому вопросу в главе 12.)

В классе его собственные переменные экземпляра являются глобальными по отношению ко всем методам экземпляра. Любой метод экземпляра класса Dog может просто использовать эту переменную number, как любую другую. Но код, с помощью которого это делается, может вас смутить: вы видите переменную по имени number и не понимаете, что это такое, потому что поблизости нет никакого объявления этой переменной. Поэтому я часто использую различные обозначения наподобие такого: self->ivarName. Оператор “стрелка”, образованный знаками “минус” и “больше”, называется оператором указателя на структуру в связи с его исходным применением в С (K&R 6.2).

Еще одно соглашение об именовании переменных экземпляра — их имена начинаются с символа подчеркивания: _ivarName. Таким образом, даже если вы не напишете self->_ivarName, одно имя _ivarName уже будет содержать подсказку о том, где может быть объявлена эта переменная.

Тот факт, что переменные экземпляров глобальны для всех методов экземпляров класса и что они сохраняются до тех пор, пока существует сам экземпляр, является вполне достаточной причиной для наличия переменных экземпляров. Рассматривайте переменные экземпляров как удобный и мощный способ совместного использования значений различными методами экземпляров одного и того же класса. Вам часто придется в одном методе экземпляра присваивать значение переменной экземпляра с тем, чтобы позже другой метод мог им вое* пользоваться. Эта возможность становится особенно важной в мире каркаса Cocoa, управляемом событиями; см. главу 11.

Однако переменные экземпляров могут также служить средством общения экземпляра с другим экземпляром вне данного. При создании кода вашего класса и размышлениях о месте последнего в вашей программе вы можете захотеть позволить экземплярам других классов получать (или даже устанавливать) значение переменных у экземпляров данного класса. Однако по умолчанию переменные экземпляров являются защищенными; это значит, что другие классы, за исключением подклассов данного класса, не могут видеть переменные экземпляров данного класса. Если же переменные экземпляров объявлены в разделе реализации класса, то никакие другие классы, даже подклассы данного, не могут их видеть. Так что если где-то в другом месте я создаю экземпляр класса Dog, я не смогу получить доступ к переменной number этого экземпляра класса Dog. Это преднамеренно спроектированная особенность языка Objective-C; при желании ее можно обойти, но в общем случае вы не должны этого делать. Вместо этого следует предоставить методы доступа к переменным экземпляров.

Для именования этих методов имеется свое соглашение: они должны иметь имена setXxx: и ххх, где иххх” совпадает у обоих имен (и может совпадать с именем переменной экземпляра). Например, наши методы доступа могут именоваться setNumber: и number.

Давайте запишем в разделе реализации класса Dog метод доступа setNumber:, который позволяет устанавливать значение переменной экземпляра number:

- (void) setNumber: (int) n {
self->number * n;
}

Конечно, для того чтобы сделать метод setNumber: открытым для любого другого класса, который импортирует файл Dog.h интерфейса класса Dog, мы должны также объявить его в разделе интерфейса Dog:

0interface Dog : NSObject
- (void) setNumber: (int) n;
@end

Теперь можно в любом импортирующем файл Dog. h классе создать экземпляр Dog и присвоить этому экземпляру номер:

Dog* fido = [Dog new];
[fido setNumber: 42];

Теперь мы можем установить значение переменной экземпляра number класса Dog, но не можем получить его (извне этого экземпляра класса Dog). Для того чтобы исправить эту оплошность, напишем второй метод доступа, number, который позволяет получать значение переменной экземпляра number:

- (int) number {<
return self->number;
}

Мы вновь объявляем этот метод в разделе интерфейса Dog. Теперь в любом классе, который импортирует файл Dog. h, мы можем как устанавливать, так и получать значение переменной экземпляра number класса Dog:

Dog* fido » [Dog new];
[fido setNumber: 42];
int n - [fido number]; // Конечно же, n равно 42!

К счастью, современная версия языка Objective-C предоставляет механизм для автоматической генерации методов доступа (обсуждаемый в главе 12), так что вам не надо долго и утомительно создавать их вручную всякий раз, когда вы хотите сделать некоторую переменную экземпляра общедоступной. (Хотя, чтобы быть честным, должен сказать, что не вижу особых причин, по которым вы не должны пройти через эту скуку; до появления версии Objective-C 2.0 мы все делали это, так почему вы должны избежать этого? Пока что вы еще дети в программировании и должны пройти суровую школу и на своей шкуре почувствовать, что это такое — реальное программирование.)

В моем коде класс Dog имеет одновременно и метод number, и переменную экземпляра number. Этот факт не должен вас смущать. И он совершенно не смущает компилятор, потому что имя метода и имя переменной экземпляра используются в коде совершенно различными способами. Если компилятор может увидеть разницу, то вы и подавно. Тем не менее применение упомянутого мною выше соглашения об именовании переменных экземпляров устраняет любую возможную путаницу. Вспомните — мы начинаем имена наших переменных экземпляров с символа подчеркивания: _number, а не number. Следуя соглашению, нам надо также переписать (но не переименовать) методы доступа к переименованной переменной экземпляра:

3implementation Dog { int _number;
}
- (void) setNumber: (int) n (
self->_number = n;
)
- (int) number {
return self->_number;
}
@end

Все переменные экземпляра при создании экземпляра получают значение, представляющее собой ту или иную разновидность нуля (как часть вызова метода класса alloc). Это важно, потому что означает, что, в отличие от локальной автоматической переменной, простое объявление переменной экземпляра без указания начального значения не опасно — что хорошо, хотя бы потому, что такое объявление невозможно. Мы не можем сказать:

(^implementation Dog (
int _number = 42; // Нет, простите, это не Objective-C!
)

Однако по крайней мере мы знаем, что переменная _number начнет свое существование, имея значение, равное 0, а не некоторому опасно неопределенному значению. Точно так же экземпляр переменной типа B00L начинает существование как N0, разновидность нулевого значения типа B00L, а переменная экземпляра, которая представляет собой объект, получит значение nil. Если вы хотите получить ненулевое значение переменной экземпляра при создании экземпляра, напишите код, который присваивает ей это ненулевое значение. И не забывайте выполнять проверки на равенство переменных экземпляров нулю (или nil).


 

 

 

Добавить комментарий