Интерфейс и реализация класса вполне могут находиться в одном и том же файле, как и несколько классов могут быть определены в одном файле, но обычно это не так.

Распространенным стилем программирования является соглашение “один класс — два файла”: в одном файле содержится раздел интерфейса, а во втором — раздел реализации. Файл реализации импортирует заголовочный файл (см. директиву # import в главе 1), что эффективно объединяет воедино определение класса, несмотря на то, что оно разделено между двумя файлами.

Предположим, например, что мы определили класс MyClass. В таком случае у нас обычно имеются два файла, MyClass .h и MyClass .m. (Именование файла именем класса не является необходимым — это просто достаточно удобно.) Раздел интерфейса находится в файле MyClass. h, который называется заголовочным файлом. Раздел реализации размещается в файле MyClass .m, который носит название файл реализации. Файлы реализации импортируют заголовочные файлы. Разделение на два файла ничуть не мешает, так как Xcode, ожидая, что вы будете следовать описанному соглашению, упрощает переход от редактируемого .h-файла к соответствующему .m-файлу, и наоборот (Navigate^Jump to Next Counterpart).

При таком размещении легко настраивать дальнейший импорт. Заголовочный файл импортирует базовый заголовочный файл для всего каркаса Cocoa; в случае программы для iOS это файл UIKit. h (см. главу 1). Файлам реализации нет необходимости импортировать UIKit. h, поскольку его импортирует заголовочный файл, который, в свою очередь, импортируется файлом реализации. Если классу надо знать о другом классе, который не импортируется таким способом, он должен импортировать заголовочный файл этого класса.

В примере 4.1 приведен пример описанной схемы; в нем я предполагаю наличие другого класса, MyOtherClass, к которому должен иметь возможность обратиться код MyClass.

 

Пример 4.1. Типичная схема определения класса

// MyClass.h:
#import cUIKit/UIKit.h>
@interface MyClass : NSObject
- (NSString*) sayGoodnightGracie;
Send
// MyClass.m:
#import "MyClass.h"
#import "MyOtherClass.h"
Simplementation MyClass {
// Здесь располагаются объявления переменных экземпляра
)
- (NSString*) sayGoodnightGracie (
return @"Good night, Gracie!";
)
Send

Результатом такого размещения является то, что все оказывается корректно видимым. Никакой файл не импортирует файл реализации; таким образом, все, находящееся в этом файле реализации, оказывается закрытым от посторонних и доступно только данному классу. Если один класс должен общаться с другим классом, он импортирует его заголовочный файл. В примере 4.1:

  • Заголовочный файл класса MyClass импортирует файл UIKit .h, поскольку в противном случае класс MyClass не может быть объявлен как унаследованный от класса NSObject.
  • Файл реализации класса MyClass импортирует файл MyClass .h, поскольку в противном случае объявление класса будет неполным. В качестве полезного побочного эффекта класс MyClass может таким образом общаться с классом NSString и другими классами, встроенными в каркас UIKit, не импортируя при этом файл UIKit. h.
  • Файл реализациикласса MyClass импортирует файл MyOtherClass. h, поскольку в противном случае класс MyClass не может работать с классом MyOtherClass (что, как мы полагаем, он должен делать).

Небольшая проблема возникает тогда, когда заголовочный файл должен упоминать класс, который он при этом не должен импортировать. Предположим, например, что класс MyClass имеет открытый метод, который принимает или возвращает экземпляр класса MyOtherClass, так что в файле MyClass .h необходимо упомянуть об указателе MyOtherClass*; но мы предпочли бы, чтобы файл MyClass .h не импортировал файл MyOtherClass .h. Но если файл MyClass.h ничего не знает о файле MyOtherClass, то при использовании этого имени компилятор сообщит об ошибке. Для того чтобы заставить компилятор промолчать, можно воспользоваться директивой @ class. За словом @ с lass следует список разделенных запятыми имен классов, заканчивающийся точкой с запятой. Так что MyClass .h может начинаться так:

#import <UIKit/UIKit.h>
Sclass MyOtherClass;

Затем, как и ранее, следует раздел интерфейса. Директива 0class просто говорит компилятору: “Не беспокойся, встретив слово MyOtherClass, — это имя реально существующего класса”. Это все, что достаточно знать компилятору, чтобы разрешить упоминание типа MyOtherClass* в заголовочном файле.

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

#define MYKEY @"mykey"

Возникает вопрос: где следует разместить это определение? Если о нем должен знать только один класс, определение может находиться в начале файла реализации (ему не нужно находиться в самом разделе реализации). Но если об этом имени должны знать несколько классов, то наиболее подходящим местом является заголовочный файл; каждый файл реализации, который импортирует этот заголовочный файл, будет видеть указанное макроопределение, и вы сможете использовать имя MYKEY в этом файле реализации.

(Если знать об определении должно много файлов, его может быть более удобно разместить в . pch-файле проекта, единого “предкомпилированного заголовка”, который неявно импортирует все . h-файлы Более подробно о . pch-файлах будет рассказано в следующих статьях.)

 

Заголовочные файлы каркаса Cocoa

Классы каркаса Cocoa также следуют соглашению, описанному в примере 4.1: каждый класс разделен на заголовочный файл (содержащий интерфейс) и файл реализации. Однако файлы реализации классов каркаса Cocoa нам не видны. Это одно из основных ограничений каркаса Cocoa; в отличие от многих других каркасов программирования, вы не можете увидеть исходный код каркаса Cocoa — он является секретом для программиста. Для того чтобы понять, как работает каркас Cocoa, вам придется полностью положиться на документацию (и эксперименты). Однако заголовочные файлы каркаса Cocoa открыты, и вам стоит их просмотреть, так как они могут оказаться полезным дополнением к документации.


 

 

 

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