Xcode 11 перекомпилирует слишком много - PullRequest
12 голосов
/ 25 марта 2020

Xcode 11 перекомпилирует (почти?) Весь мой проект, даже если я просто изменяю локальную частную переменную или изменяю значение константы в локальной области видимости, иногда даже в области локальной частной функции. Иногда я могу получить 2 или 3 изменения с быстрыми сборками, как и ожидалось, но достаточно скоро он решает перекомпилировать все заново (что занимает слишком много времени).

Есть идеи, что может произойти? Разве Xcode не может определить, что изменилось, почему он перекомпилирует так много других вещей (даже других модулей).

Любой совет очень важен, спасибо!

Ответы [ 2 ]

8 голосов
/ 03 апреля 2020

У нас была такая же проблема, и мы ее исправили. Дважды.

Инкрементная сборка (та же машина сборки):

до: ~ 10 м после: ~ 35 с

КАК?

Давайте сначала начнем с нашего опыта. У нас был огромный проект Swift / Obj- C, и это было главной заботой: время сборки было медленным, и вам пришлось создавать новый проект для реализации новой функции (буквально). Бонусные баллы за неработающую подсветку синтаксиса.

Теория

Чтобы действительно это исправить, вы должны по-настоящему понять , как работает система сборки. Например, давайте попробуем этот фрагмент кода:

import FacebookSDK
import RxSwift
import PinLayout

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

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

Dependency tree

Xcode build многопоточный , но состоит из множества однопоточных деревья .

Итак, на первом шаге каждой инкрементальной сборки Xcode решает, какие файлы необходимо перекомпилировать, и создает дерево AST . Если вы измените файл, который действует как « зависящий » для других файлов, то каждый другой файл, который действует как « зависимый », должен быть перекомпилирован.

Coupling

Итак, первый совет: нижняя муфта . Части вашего проекта должны быть независимы друг от друга.

Мост Obj-C / Swift

Проблема с этими деревьями, если вы используете мост Obj-C / Swift, Xcode должен go через больше фаз, чем обычно:

Идеальный мир:

  1. Builds Obj- C код
  2. Build Swift код

Swift/Obj-C bridge

Мост Obj-C / Swift:

  1. [REPEATABLE STEP] Построить код Swift, необходимый для компиляции Obj- C код
  2. [REPEATABLE STEP] Build Obj- C код, необходимый для компиляции кода Swift
  3. Повторяйте 1 и 2 до тех пор, пока у вас не останется только надежный Swift & Obj- C оставленный код
  4. Build Obj- C код
  5. Build Swift код

Obj-C/Swift bridge

Так что если Вы меняете что-то с шага 1 или 2, у вас в основном проблемы. Лучшее решение - минимизировать Obj-C / Swift Bridge (и удалить его из вашего проекта).

Если у вас нет Obj-C / Swift Bridge, это здорово, и вы хороши для go до следующего шага:

Диспетчер пакетов Swift

Время перейти на SwiftPM (или, по крайней мере, лучше настроить ваши Cocoapods).

Дело в том, что большинство фреймворков по умолчанию Конфигурация Cocoapods тащит за собой множество вещей, которые вам не нужны.

Чтобы проверить это, создайте пустой проект с одной зависимостью, такой как PinLayout, например, и попробуйте написать этот код с помощью Cocoapods (конфигурация по умолчанию) и SwiftPM.

import PinLayout

final class TestViewController: UIViewController {

}

Spoiler: Cocoapods скомпилируйте этот код, потому что Cocoapods будет импортировать КАЖДЫЙ ИМПОРТ PinLayout (включая UIKit), а SwiftPM не будет, потому что SwiftPM импортирует фреймворки атомарно.

Dirty hack

Помните, сборка Xcode является многопоточной?

Что ж, вы можете злоупотребить этим, если вам удастся разделить ваш проект на множество независимых частей и импортировать все из них как независимые фреймворки в ваш проект. Это снижает связь, и это было фактически первое решение, которое мы использовали, но на самом деле оно было не очень эффективным, потому что мы могли только сократить время инкрементной сборки до ~ 4-5 м, что НИЧЕГО по сравнению с первым методом.

3 голосов
/ 01 апреля 2020

Здесь нет золотой пули, но есть много вещей, которые нужно проверить:

  • Убедитесь, что вы действительно используете конфигурацию Debug в своей схеме Xcode Scheme Editor using the Debug configuration

  • См. Ниже, как убедиться, что вы используете инкрементные сборки по сравнению с целым модулем по совету Мэтта. Также убедитесь, что ваш Уровень оптимизации для отладочных сборок равен none. Xcode Build settings showing Incremental Builds

  • Если вы используете тяжелые фреймворки с выводом типа, такие как RxSwift, добавление явных аннотаций типов может ускорить время сборки.

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

Может помочь, если вы предоставите более подробную информацию о проекте: вы статически связываете какие-либо библиотеки? Это фреймворк или цель приложения? Насколько большой и какой быстрой версией вы пользуетесь? Есть ли у вас какие-либо пользовательские фазы сборки, такие как линтеры или генерация кода, которые иногда можно пропустить?

...