Лучшие практики iOS Prefix.pch

90

Я видел много разработчиков, которые добавляли различные удобные макросы в Prefix.pch своих проектов iOS.

Что вы рекомендуете (или нет) добавлять в файл iOS Prefix.pch? Как выглядит ваш Prefix.pch?

hpique
источник
Подробное сообщение в блоге на эту тему: cimgf.com/2010/05/02/my-current-prefix-pch-file
hpique
2
Просто поместите свои макросы, например Macros.h, в файл заголовка , а затем импортируйте этот файл в свой prefix.pch.
Malloc
Я также сталкиваюсь с той же проблемой ... как решить в Xcode 6.1
Яламандарао

Ответы:

121

Фуу… не помещайте макросы в файл .pch! Файл .pch по определению представляет собой предварительно скомпилированный заголовок конкретного проекта. Его действительно не следует использовать вне контекста проекта, и он действительно не должен содержать ничего, кроме #includes и#import s.

Если у вас есть макросы и такие, которые вы хотите использовать в заголовках, вставьте их в отдельный файл заголовка - Common.hили что-то еще - и #include это в начале .pch.

бомж
источник
Что бы вы включили в этот Common.h?
hpique
4
Ничего; Я бы только добавил туда различные #defines и т. Д.
bbum
37

Для современных iOS и OS X люди должны использовать модули . По умолчанию это включено для новых проектов, а импорт / включение выполняется с использованием @import.

Модули позволяют компилятору создавать промежуточное представление содержимого модуля (например, заголовки фреймворка). Подобно PCH, это промежуточное представление может использоваться в нескольких переводах. Но модули делают еще один шаг вперед, потому что модуль не обязательно является целевым, и их объявления не нужно локализовать (в a *.pch). Это представление может сэкономить массу лишней работы компилятора.

Используя модули, вам не нужен PCH, и вам, вероятно, следует просто полностью отказаться от них - в пользу использования @importlocal для зависимости. В этом случае PCH только спасает вас от ввода локальных включений для зависимостей (что IMO вы все равно должны делать).

Теперь, если мы вернемся к исходному вопросу: вам следует избегать заполнения вашего PCH всевозможными случайными вещами; Макросы, константы #definesи всевозможные маленькие библиотеки. Как правило, вы должны опускать то, что действительно не нужно для большинства ваших исходных файлов . Размещение всевозможных вещей в вашем PCH просто добавляет кучу веса и зависимости. Я вижу, как люди помещают все, на что они ссылаются, и многое другое в PCH. На самом деле, в большинстве случаев вспомогательные фреймворки должны быть видимы только для нескольких переводов. Например: «Вот наш материал StoreKit - давайте импортируем StoreKit только там, где он долженбыть видимым. В частности, эти 3 перевода ". Это сокращает время сборки и помогает отслеживать ваши зависимости, чтобы вам было легче повторно использовать код. Так что в проекте ObjC вы обычно останавливаетесь на Foundation. Если их много пользовательского интерфейса, то вы можете рассмотреть возможность добавления UIKit или AppKit на свой PCH. Все это предполагает, что вы хотите оптимизировать время сборки. Одна из проблем с большими PCH, которые включают (почти) все, заключается в том, что удаление ненужных зависимостей занимает очень много времени. зависимости вашего проекта растут, и время сборки увеличивается, вам нужно сопротивляться, устраняя ненужные зависимости, чтобы сократить время сборки. Кроме того, все, что часто изменяется, обычно следует держать за пределами вашего PCH. Для изменения требуется полная перестройка. Есть несколько вариантов совместного использования PCH. Если вы используете PCH,

Что касается того, что я вложил в свой PCH: я перестал использовать их для подавляющего большинства целей много лет назад. Просто обычно не хватает общего, чтобы квалифицироваться. Имейте в виду, что я пишу C ++, ObjC, ObjC ++ и C - компилятор выдает по одному для каждого языка вашей цели. Таким образом, их включение часто приводило к более медленному времени компиляции и увеличению количества операций ввода-вывода. В конечном счете, увеличение зависимости - не лучший способ бороться с зависимостью в сложных проектах. При работе с несколькими языками / диалектами существуют большие различия в зависимостях, необходимых для данной цели. Нет, я бы не советовал это оптимизировать для каждого проекта, но это дает некоторую перспективу управлению зависимостями в более крупных проектах.


Рекомендации


Ноты

  • Первоначально этот вопрос был задан за несколько лет до появления модулей.
  • В настоящее время (Xcode 5.0) модули работают для C и ObjC, но не для C ++.
Джастин
источник
Что означает полная перестройка для проекта с включенным модулем. Вы знаете, что этот новый заголовок моста -Swift.h определенно не подходит для .pch. Но я видел, как люди так поступали. Итак, как вы можете видеть, если кто-то это делает. У нас всегда меняющийся заголовок в .pch. Таким образом, он перестраивает все в файле .pch каждый раз при регенерации -Swift.h. ты согласен с этим? У вас есть больше материалов?
MadNik 03
@MadNik Предположим, что PCH вашего приложения включает главный заголовок библиотеки / фреймворка, который вы активно разрабатываете. Например: #import "MyLib/MyLib.h". MyLib.hКаждый раз, когда файл включается изменениями, каждый исходный файл в приложении необходимо перекомпилировать. Если вы используете MyLib только в одном исходном файле, то при изменении MyLib необходимо перекомпилировать только этот файл. Хотите верьте, хотите нет, но в наши дни я не использую файлы PCH.
Джастин
8

Я согласен с bbum. Я считаю, что файл PCH должен содержать в основном только операторы #includeили #import. Так что, если у вас есть набор полезных макросов высокого уровня, определите их в чем-то вроде Common.hи #importэтого файла, как предлагает bbum.

Я обычно иду на шаг дальше и использовать файл PCH в #importфайл с именем XXCategories.h(где XXесть класс имена префикс конвенция используется) , который содержит #importS для всех моих UIKit и класса Foundation категорий: NSString+XXAdditions.h, UIColor+XXAdditons.hи т.д.

CIFilter
источник
Мне просто любопытно. В файле .PCH, в чем разница между импортом common.h , который имеет различные #importи просто импортировать те #importнапрямую? Разве это не то же самое? Или это влияет на производительность?
Hlung
Насколько мне известно, реальной разницы нет. Думаю, это скорее лучшая практика. Вместо того, чтобы вставлять кучу макросов и прочего в ваш файл PCH, он должен быть только для #importи #include.
CIFilter
1
Разница в возможности повторного использования. PCH зависит от проекта. Common.h будет общим для многих проектов. Это все равно, что спрашивать, почему вы просто не помещаете все свои служебные классы в свой проект вместо создания подпроекта, который можно использовать повторно. Хотя это и надуманный пример, потому что это всего лишь простая копипаста ... но копипаст - непослушный.
bandejapaisa
6

создать файл заголовка "macros.h"

импортировать этот заголовок в Prefix.pch

В этот macros.h поместите все фреймворки и другие важные вещи.

Если вас беспокоит производительность, не волнуйтесь, посмотрите, что говорит яблоко:

Заголовки и производительность

Если вас беспокоит, что включение главного файла заголовка может привести к раздуванию вашей программы, не волнуйтесь. Поскольку интерфейсы OS X реализованы с использованием фреймворков, код для этих интерфейсов находится в динамической разделяемой библиотеке, а не в вашем исполняемом файле. Кроме того, только код, используемый вашей программой, когда-либо загружается в память во время выполнения, поэтому ваш размер в памяти также остается небольшим. Что касается включения большого количества файлов заголовков во время компиляции, опять же, не беспокойтесь. Xcode предоставляет возможность предварительно скомпилированного заголовка для ускорения времени компиляции. При одновременной компиляции всех заголовков фреймворка нет необходимости перекомпилировать заголовки, если вы не добавите новый фреймворк. В то же время вы можете использовать любой интерфейс из включенных фреймворков с незначительным снижением производительности или без него.

также в моем macros.h я поместил много констант, например:

// delegate
#define UIAppDelegate (AppDelegate *)[[UIApplication sharedApplication] delegate]
#define APPDELEGATE   ((AppDelegate *)[[UIApplication sharedApplication] delegate])

// system
#define IS_IPHONE_4INCH (UI_USER_INTERFACE_IDIOM()==UIUserInterfaceIdiomPhone && [UIScreen mainScreen].bounds.size.height==568)
#define IS_IPAD                     (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)

// screen size
#define IS_IPAD (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
#define IS_IPHONE (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
#define IS_IPHONE_4 (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 480.0)
#define IS_IPHONE_5 (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 568.0)
#define IS_IPHONE_6 (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 667.0)
#define IS_IPHONE_6PLUS (IS_IPHONE && [[UIScreen mainScreen] nativeScale] == 3.0f)
#define IS_IPHONE_6_PLUS (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 736.0)
#define IS_RETINA ([[UIScreen mainScreen] scale] == 2.0)
#define IS_RETINA_DISPLAY ([[UIScreen mainScreen] respondsToSelector:@selector(displayLinkWithTarget:selector:)] && ([UIScreen mainScreen].scale == 2.0))
#define IS_PORTRAIT                 UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation])
#define IS_LANDSCAPE                UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])

//system version
#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
#define SYSTEM_VERSION_GREATER_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)

// math
#define DEGREES_TO_RADIANS(angle) ((angle) / 180.0 * M_PI)
#define RADIANS_TO_DEGREES(radians) ((radians) * (180.0 / M_PI))

// cores
#define RGB(r,g,b)    [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1]
#define RGBA(r,g,b,a) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:a]
#define MAKECOLOR(R, G, B, A) [UIColor colorWithRed:((float)R/255.0f) green:((float)G/255.0f) blue:((float)B/255.0f) alpha:A]
#define MAKECOLORFROMHEX(hexValue) [UIColor colorWithRed: ((float)((hexValue & 0xFF0000) >> 16))/255.0 green:((float)((hexValue & 0xFF00) >> 8))/255.0 blue:((float)(hexValue & 0xFF))/255.0 alpha:1.0]



//customizations
#define SHOW_STATUS_BAR               [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationNone];
#define HIDE_STATUS_BAR               [[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationNone];

#define SHOW_NAVIGATION_BAR           [self.navigationController setNavigationBarHidden:FALSE];
#define HIDE_NAVIGATION_BAR           [self.navigationController setNavigationBarHidden:TRUE];

#define VC_OBJ(x) [[x alloc] init]
#define VC_OBJ_WITH_NIB(x) [[x alloc] initWithNibName : (NSString *)CFSTR(#x) bundle : nil]

#define RESIGN_KEYBOARD [[[UIApplication sharedApplication] keyWindow] endEditing:YES];

#define CLEAR_NOTIFICATION_BADGE                       [UIApplication sharedApplication].applicationIconBadgeNumber = 0;
#define REGISTER_APPLICATION_FOR_NOTIFICATION_SERVICE  [[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)]

#define HIDE_NETWORK_ACTIVITY_INDICATOR                 [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
#define SHOW_NETWORK_ACTIVITY_INDICATOR                 [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
Лео Кавальканте
источник
2
Еще одно полезное здесь: #define async(...) dispatch_async(dispatch_get_main_queue(), __VA_ARGS__ )... для запуска блоков в основном потоке:async(^{ self.someLabel.text = @":D"; });
Алехандро Иван,