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

Поэтому большая программа на языке программирования С обычно состоит из двух типов файлов: файлы с кодом, расширение имени файла которых обычно .с, и заголовочные файлы с расширением .h. Система построения автоматически будет “видеть” все файлы и будет знать, что все вместе они составляют одну программу. Однако при этом существует правило, согласно которому код в одном файле не может “видеть” другой файл, если только ему явным образом не указано иное. Таким образом, сам по себе файл представляет область видимости. Это преднамеренная (и весьма ценная) возможность языка С, потому что позволяет поддерживать порядок в вашем коде.

Для того чтобы один файл в С “увидел” другой файл, ему надо явно сказать об этом с помощью директивы linclude. Знак # указывает, что данная строка представляет собой директиву препроцессора. В данном случае слово #include, за которым следует имя другого файла, является директивой, которая заставляет препроцессор просто заменить эту директиву полным содержимым указанного в директиве файла.

Стратегия построения большой программы на языке программирования С выглядит примерно следующим образом.

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

Так, например, если функция functionl определена в файле filel.c, но в файле file2.c может потребоваться вызов функции functionl, то объявление функции functionl можно поместить в файл f ilel. h. Теперь файл f ilel. с может включить файл filel .h, так что все его функции могут вызывать функцию functionl независимо от расположения в файле по отношению к определению этой функции. Файл f ile2 . с также может включить файл f ilel. h, так что все его функции также смогут вызывать функцию functionl (рис. 1.2). Короче говоря, заголовочные файлы представляют собой способ обеспечить возможность совместного использования файлами с кодом знаний о другом коде без реального совместного использования самого кода (поскольку в случае совместного использования кода теряется сам смысл разделения кода на отдельные файлы).

 Разделение большой программы на файлы

Рис. 1.2. Разделение большой программы на файлы

Но откуда компилятор знает, где среди всего множества . с-файлов найти место, с которого надо начать выполнение программы? Каждая реальная программа на языке программирования С содержит в каком-то месте ровно одну функцию с именем main, и она всегда является точкой входа для программы в целом; компилятор настраивает все так, что при выполнении программы вызывается функция main.

Описанная мною организация больших программ на языке программирования С, по сути, такая же, как и для ваших программ для iOS. Главное отличие будет в том, что вместо . с-файлов вы будете использовать . m-файлы, потому что по соглашению именно расширение . ш говорит среде Xcode, что это файл на языке программирования Objective-C, а не на языке С.

Кроме того, если вы взглянете на любой проект Xcode для iOS, вы обнаружите, что он содержит файл с именем main .ш; и если вы рассмотрите содержимое этого файла, то увидите, что он содержит функцию с именем main. Это точка входа в код приложения при его запуске.

Существенное отличие между файлами с кодом Objective-C и кодом С заключается в том, что вместо директивы #include в файлах Objective-C используется директива #import. Директива препроцессора #import не упоминается в K&R. Это дополнение Objective-C к языку программирования С. Она основана на директиве #include, но используется вместо нее, потому что содержит определенную логику для того, чтобы один и тот же материал не был считан и не вошел в файл с кодом более одного раза. Такое повторное включение представляет собой опасность, когда есть много зависимых один от другого заголовочных файлов; использование директивы #import позволяет аккуратно справиться с этой проблемой.

Кроме того, ваша программа для iOS состоит не только из ваших файлов кода и соответствующих им заголовочных файлов, но и файлов кода и соответствующих им заголовочных файлов Apple. Разница в том, что файлы кода Apple (которые составляют каркас Cocoa, см. часть III) уже скомпилированы. Однако ваш код должен по-прежнему использовать директиву #import для заголовочных .h-файлов Apple, чтобы иметь возможность видеть и использовать объявления Apple. Если вы взглянете на проект Xcode для iOS, то увидите, что любые . h-файлы, содержащиеся в нем по умолчанию, а также файл main .m содержат строку вида

#import <UIKit/UIKit.h>

Эта строка по сути представляет собой единую массивную директиву #import, которая копирует в вашу программу объявления всего базового API iOS. Более того, каждый из ваших . m-файлов импортирует соответствующий . h-файл, включая все импортируемое этим . h-файлом. Например, если у вас есть файл AppDe legate .m, он содержит строку

#import "AppDelegate.h"

Однако файл AppDelegate.h импортирует <UIKit/UIKit.h>. Таким образом, все ваши файлы с кодом включают все основные объявления iOS.

Как показывают приведенные примеры, директива #import, как и директива #include (K&R 4.11), может содержать имя файла в угловых скобках и в кавычках. Разные ограничители имени по-разному влияют на работу компилятора.

Кавычки. Компилятор ищет указанный файл р той же папке, где находится и включающий файл (.m-файл, в котором расположена строка #import).

Угловые скобки. Компилятор ищет указанный файл среди различных путей для поиска файлов, указанных в настройках компилятора. (Эти пути устанавливаются автоматически, и обычно вам не требуется их изменять.)

В общем случае угловые скобки используются для заголовочных файлов, владельцем которых является интерфейс API каркаса Cocoa, а кавычки — для заголовочных файлов, написанных вами. Если вам интересно, что именно импортирует директива # import, щелкните, удерживая нажатой клавишу <Command>, на имени импортируемого файла, чтобы просмотреть непосредственно включаемый файл.

В системе iOS 7 и среде Xcode 5 имеется дополнительный механизм импорта — модули, которые используются неявно, “за сценой”, как часть процесса построения любого нового проекта iOS (и могут быть включены в старых проектах через настройки построения). Но вам никогда не придется непосредственно соприкасаться с ними. Основное назначение модулей — ускорение компиляции ваших проектов.

Без модулей процесс компиляции должен начинаться с буквального включения заголовочных файлов для UIKit и iOS API в каждом из ваших файлов. Например, ранее я говорил, что CGPoint определен как

struct CGPoint (
CGFloat x;
CGFloat y;
} ;
typedef struct CGPoint CGPoint;

После обработки ваших файлов препроцессором .m-файлы содержат определение CGPoint буквально — именно поэтому ваш код может использовать этот тип данных. Определение CGPoint и всех прочих импортируемых материалов добавляет к каждому из ваших файлов более 30 ООО строк кода, с которым приходится иметь дело компилятору. Модули позволяют снизить накладные расходы. Импортируемый материал компилируется один раз, автоматически (обычно при создании или открытии вашего проекта), и кешируется в отдельном месте; этот кешированный код состоит из модулей. В процессе построения препроцессор вставляет в ваш код только несколько строк, таких как

@import UIKit;
@import Foundation;

(Для подтверждения можете выбрать Products Perform Action^ Preprocess [Имя файла].) Директива компилятора 0import с символом @ вместо # является новинкой в среде Xcode 5 и обращается к модулю по его имени. Поскольку модуль уже скомпилирован, никакой дополнительной работы компилятору делать не приходится. Более подробно о директиве @import и модулях речь пойдет в главе 6.

 

Объявления функций и методов в современном Objective-C

Начиная с компилятора LLVM версии 3.1, который был представлен в среде Xcode 4.3, Objective-C больше не требует, чтобы объявление функции предшествовало ее использованию, если определение этой функции располагается позже в этом же файле. Это относится как к функциям С, так и к методам Objective-C. Другими словами, код внутри класса Objective-C может вызывать функции С или методы Objectlve-C, даже если этот вызов предшествует определению данной функции или метода, и при этом не имеется отдельного объявления указанной функции или метода. Таким образом, в современном Objectlve-C порядок функций и методов в .ш-файле не имеет значения, и даже не требуется объявлений функций и методов в .m-файле вовсе! Единственное место, где вы должны объявить функцию или метод, — .h-файл, так что .m-файл, отличный от того, в котором эта функция или метод определены, для возможности ее вызова должен всего лишь импортировать этот заголовочный файл.

 

Это соглашение является возможностью Objectlve-C, но не С. Я говорю только о . m-файлах, но не о . с-файлах. Более подробно о том, где в .ш-файлах применимо это соглашение (а именно в разделе реализации класса), я расскажу в главе 4.


 

 

 

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