Objective-C Runtime. Теория и практическое применение
Что такое Runtime?
Objective-C задумывался как надстройка над языком C, добавляющая к нему поддержку объектно-ориентированной парадигмы. Фактически, с точки зрения синтаксиса, Objective-C — это достаточно небольшой набор ключевых слов и управляющих конструкций над обычным C. Именно Runtime, библиотека времени выполнения, предоставляет тот набор функций, которые вдыхают в язык жизнь, реализуя его динамические возможности и обеспечивая функционирование ООП.
Базовые структуры данных
Мы видим, что объект в процессе работы программы представлен обычной C-структурой. Каждый Objective-C объект имеет ссылку на свой класс — так называемый isa-указатель. Думаю, все видели его при просмотре структуры объектов во время отладки приложений. В свою очередь, класс также представляет из себя аналогичную структуру:
Класс в Objective-C — это полноценный объект и у него тоже присутствует isa-указатель на «класс класса», так называемый метакласс в терминах Objective-C. Аналогично, С-структуры определены и для других сущностей языка:
Функции Runtime-библиотеки
Пример 1. Интроспекция объекта
Рассмотрим пример использования Runtime библиотеки. В одном из наших проектов модель данных представляет собой plain old Objective-C объекты с некоторым набором свойств:
2013-05-04 15:54:01.992 Test[40675:11303] COConcreteObject: <
name = Foo;
quantity = 10;
title = bar;
>
Сообщения
Система вызова методов в Objective-C реализована через посылку сообщений объекту. Каждый вызов метода транслируется в соответствующий вызов функции objc_msgSend :
Здесь вы можете динамически указать свою реализацию вызываемого метода. Если же этот механизм по каким-то причнам вас не устраивает — вы можете использовать форвардинг сообщений.
Пример 2. Method Swizzling
Пример 3. Ассоциативные ссылки
Еще одним известным ограничением категорий является невозможность создания в них новых переменных экземпляра. Пусть, например, вам требуется добавить новое свойство к библиотечному классу UITableView — ссылку на «заглушку», которая будет показываться, когда таблица пуста:
«Из коробки» этот код работать не будет, вы получите исключение во время выполнения программы. Эту проблему можно обойти, используя функционал ассоциативных ссылок:
Что такое компиляция, линковка, run time?
Компиляция и линковка
Компиляция – это процесс превращения исходного кода (который написал программист) в код, понятный компьютеру. Java, например, компилируется в код, понятный не компьютеру, а Java Virtual Machine. Но это не суть, главное в итоге получается код, который может прочесть машина.
В большинстве случаев при написании кода вы подключаете какие-то библиотеки. Для того чтобы библиотека была доступна вашему коду, вы ее каким-то образом импортируете. В разных языках есть разные конструкции импорта. После подключения, ваш код может скомпилироваться, потому что ваш код соединяется с кодом библиотеки, т.е. компилируется на его основе. В результате вы получаете бинарный код библиотеки и ваш код программы, который лежит отдельно. Если говорим про Джаву, то так все и остается, связывание кодов происходит дальше в джава машине в процессе выполнения программы. Но в некоторых языках связывание кода требуется до запуска программы. Процесс связывания в единый файл уже скомпилированных кусков вашего кода и кода всех остальных библиотеки называется линковкой. Т.е. линковка – это соединение кода в один большой выполнимый файл. В джаве это не требуется, а в С++, например, это достаточно тяжелый процесс, который занимает достаточно много времени.
Это важные термины, но большинство программистов о них спотыкаются. Процесс, когда вы продумываете и пишете код называется design time. В этот момент код не валидный, он не скомпилируется и не сработает, потому что вы прямо сейчас работаете над ним. Но вот вы остановились, сохранили его и нажали кнопку компиляции (в Eclipse компиляция происходит автоматически при сохранении). Запускается компилятор, который для каждого file.java создает file.class с бинарным кодом, который будет выполняться джава машиной. Вот этот процесс превращения называется Compile time.
Во время Compile time машина ищет все используемые библиотеки. Если находит – компилирует их, если не находит или если в вашем коде есть ошибки, появляется сообщение типа «Ошибка компилятора». Ошибки, произошедшие в это время, называются ошибками Compile time или ошибками компиляции. Тут мы видим, что за ошибка: не нашла библиотеку или ошибка в коде, присвоение неправильного типа, забытые структуры языка и пр. Если вы видите ошибку компиляции (не warning, а именно error), то на выходе вы не получаете никаких классов, чаще всего они остаются старыми. Итак, Compile time – это время, когда исходный код превращается в бинарный. Поиском соответствий занимается джава компилятор.
Вот вы скомпилировали код, у вас лежит связка файлов, теперь их нужно запустить. Вы запускаете Джава машину (сами или это делает IDE) и в нее загружаете ваши файлы с командой Запустить. Все, что происходит в это время и дальше называется runtime.
В чем особенность. На моменте Compile time зависимости от ваших библиотек находит компилятор, у него своя система соответствия, по которой происходит компиляция. А вот в runtime зависимости от библиотек ищет уже джава машина и у нее может быть совсем другой набор мест, где нужно искать. Поэтому довольно часто получается, что после запуска программист видит, что класс не найден. Это вызывает удивление, ведь все скомпилировалось, значит все хорошо. Так получается, потому compile time и untime — абсолютно разные процессы и выполняют их разные программы. Компайл тайм делает компилятор, а рантайм делает джава машина. Поэтому программа может компилироваться без ошибок, но потом падать на этапе запуска. Это нормально, потому что компилятор не может проверить за вас правильность программы. Иначе зачем бы тогда были нужны программисты?
Что такое рантайм?
Тут у меня, по лоровской терминологии, включено 2 рантайма, сишный и плюсовой?
Ха. И тема совсем новая. Просмотрел как-то. Но дискуссия там странная и ничего все равно не понятно.
Ты прав до слов «я прав» включительно. После этого просто два заголовка, включенных в сорец. Рантайм во втором смысле в программе будет независимо от того, что и куда ты включал.
Ну и упоминание рантайма это не царская привилегия, его могут упоминать и не подобные ему.
Там больше срач о «модели выполнения», зачем-то перемешанной с рантаймом. Тсу, судя по тегам, хватит понятия sequence point, не влезая в эту мышиную возню.
Самый простой пример: GC.
чем ближе язык данной машине тем меньше(колмогорова мера вот это вот всё) рантайм.
т.е рантайм бойлерплейт исполнения для данного языка на данной машине.
например для перво компилируемых языков (фортраны там) не нужон стек на целевой машине.
Теперь должно быть ясно
В коде ниже, присваиваение переменной x происходит в момент работы программы. Все остальные действия происходят во время компиляции. Если сделать дизассемблирование, то «рантайм» в данном примере это вот эта инструкция:
после ордер \r сделай
А как вообще можно программировать с такими понятиями? Что значит сишный? Какая разница, какой язык? Ты же сам сначала говоришь
Я знаю, что рантайм это время выполнения программы, как антоним компайлтайм
Я знаю
Если ты это знаешь, то как это сочетается с той ахинеей, про libc и прочую лабуду, которую ты плетешь дальше? Или имеется в виду, знаю, читал, но не понимаю? Ну так надо так писать сразу. Создается ощущение, что си (относительно низкоуровневым же считается, да?) самый синтетический язык, оторванный от реальности из всех возможных, судя по понятиям его адептов. Например, по версии царя, ООП нужно для создания интерфейсов, а интерфейсы — для эргономичности. Может си вообще к программированию отношения не имеет?
Я знаю, что рантайм это время выполнения программы, как антоним компайлтайм.
Мне кажется, данное слово еще употребляют к стандартным библиотекам ЯП
Это второе значение, сокращение от «run-time libraries». Однако термин включает не только собственно стандартную библиотеку, но и все библиотеки, которые компилятор неявно линкует с твоей программой.
В случае с С++ ситауция немного сложнее, так как значительная часть стандартной библиотеки является header-only и поэтому к рантайму не относится. С другой стороны, посредством рантайма реализуются исключения, которые являются частью core language, а не стандартной библиотеки (см. выше про «все библиотеки. »)
включающая(но не ограниченная как правило) в себя премайн машинению и машинерию завершения программы и перечу управления обратно в ос
Для простых смертных, не-миллениалов: код, выполняющийся до функции main() и запускающий ее, и код, выполняющийся после возврата из нее.
Например, к рантайму относится код, который выполняется от точки входа в исполняемом файле до вызова функции main(). Как минимум, он откуда-то достаёт и передаёт значения argc, argv
+1 Тоже неплохое объяснение на пальцах.
execution environment = ОС в данном случае.
Как минимум, он откуда-то достаёт и передаёт значения argc, argv
Если ОПу это интересно, то берёт он (рантайм) их из стека, куда эти данные заботливо положило ядро.
Например, к рантайму относится код, который выполняется от точки входа в исполняемом файле до вызова функции main().
Опять же, если ОП сомневается, то main() это вовсе не тот кусок кода, который первым начинает выполняться при старте процесса.
Тут у меня, по лоровской терминологии, включено 2 рантайма, сишный и плюсовой?
По ЛОРовской терминологии есть ещё один рантайм: 
В дополнение рекомендую посмотреть (слегка win/msvc specific но всё равно полезно)
Выше адепт написал верно:
Please, read last comment with steady attention. Exactly,this is your level. You may watch youself there, just like in a mirror.
см. Многоуровневая организация ЭВМ.Э.Таненбаум 1979
Structured Computer Organization Andrew S. Tanenbaum 1976
Открою маленькую тайну, но main() не является истинным entry point в программе.
Для MSVC (то, что я знаю) это mainCRTStartup (или ей подобные, разные для console и GUI, wide и ansi) делающие вот это: http://stackoverflow.com/questions/1583193/what-functions-does-winmaincrtstartup-perform
Мне интересно, как он этого наркомана парсит, если кто не уловил. Про ентри поинты я узнал, когда начал exe вместо com собирать.
У вас очень хорошо получается объяснять (я не шучу)
надо думать как он, всё просто 
Пластичность мозга же — когда читаешь много постов кулинксяо, тренируется нейронная сеть и мозг создает парсер, который может переваривать его посты.
А, это. Работа со своеобразными заказчиками/инженерами придает необыкновенную гибкость парсеру
Не, я в целом это понял, можно просто выкинуть все знаки препинания и прочитать так, как будто не пишешь, а говоришь разговорно, угадывая интонацию. Но иногда она не угадывается и тогда все, reduce-reduce conflict, нерешаемая хрень.
моя после угадывания паттерна (первые 2-3 слова) перепрыгивает сразу на следующий пост =\
Нет, это crt0.S. То, что инициализирует стек для программы, ставит всякие гарды и обработчики исключений и прочее. А также это то, что помогает работать всяким конструкциям языка в период исполнения (например, сборщик мусора или поддержка блоков)
Также crt0.S (или crt1.S, что почти то же самое) заполняют аргументы для main и делают её вызов, а также вызывают exit на выходе из main.
Обычно crt0/1 — это маленькая программа на ассемблере, описывающая функцию _start, куда передаётся исполнение при запуске программы. Она достаёт argc, argv и env из начала стека, как указано тут http://www.x86-64.org/documentation/abi.pdf Во фряхе, ядро копирует %rsp в %rdi, что позволяет написать crt1 на C. Для C это почти весь рантайм, он не такой сложный, как для других языков.
Тут царь и ему подобные частенько упоминают такую штуку как рантайм. А я из тех, кто не осилил, что это значит. Я знаю, что рантайм это время выполнения программы, как антоним компайлтайм. Например, ошибка может появиться «в рантайме».
Почти. Рантайм языка это всё, что требуется для запуска программы на этом языке. Например для Java рантайм это виртуальная машина (JVM) и стандартная библиотека. Стандартная библиотека многих языков встраивается в конечный бинарник и при запуске ничего не требует, поэтому многие языки не требуют отдельного рантайма.
Формально говоря да. Но по факту практически на любом языке программа использует libc, можно и без неё обойтись, но я таких языков не встречал. Так что его рантаймом можно считать с определённой натяжкой. Рантайм часто это какие-то дополнительные мегабайты. libc есть на любом компьютере, кроме совсем уж встраиваемых, поэтому с какой-то точки зрения у C нет рантайма.
Ну и C в принципе не требует рантайма, т.е. можно писать программы и без него (хотя немножко «поколдовать» придётся). Не со всеми языками так получится.
Тут у меня, по лоровской терминологии, включено 2 рантайма, сишный и плюсовой?
Это общепринятая терминология. Да, в твоём примере два рантайма.
dmitrys99
Среды времени выполнения
Когда пишете программу, то часть функционала вам доступна сразу. Например, вам не нужно определять какие-то базовые вещи вроде
WriteLn() в Pascal или malloc() в C.
Т.е. для программиста это данность. Конечно, вполне очевидно, что для работы инструмента должны быть какие-то базовые, примитивные вещи, и это очевидно. Они и составляют среду времени выполнения или по английски runtime.
Я же предлагаю посмотреть на это несколько под другим углом.
Вот этот набор базовых, примитивных “кирпичиков” составляет некий исполнительный механизм, с которым программист взаимодействует. Этот механизм представляет собой самостоятельную программную сущность. И при таком подходе можно выделять свойства этого механизма.
Самостоятельность
Среда времени выполнения языка Java представляет собой виртуальную машину, исполняющую байткод Java. Этам машина работает как отдельная программа, для которой можно задать инструкции. Теоретически, JVM можно было бы запустить в режиме ожидания байткода и заставить исполнять инструкцию за инструкцией.
Напротив, среда времени выполнения языков C и C++ представляет собой набор библиотек, лежащих на диске и загружаемых по мере использования (в случае с DLL/SO), либо зашиваемых внутрь исполняемого файла.
Соответственно есть промежуточные варианты вроде среды языков семейства Common Lisp или Smalltalk, где среда не является исполняемой программой сама по себе, тем не менее, на момент начала работы программы часть кода загружается и может быть вызвана как отдельная инструкция.
Уровни абстракции
Языки бывают разного уровня абстракции. То же самое относится и к средам выполнения. Это значит, что на единицу информации в среде приходится разное количество полезных действий. Разумеется, одна операция на языке 1С будет существенно отличаться от операции на C.
Понимание этого факта становится важным, когда приходится по каким-то причинам сводить воедино программы на разных языках с разными средами выполнения.
Обычно лучше всего это получается для простых сред вроде C. Для более сложных сред приходится либо вкладывать одну среду внутрь другой, например при встраивании языка Lua внутрь программы на C++, либо использовать другие механизмы объединения вроде Foreign Function Interface.
Соглашения о вызовах
Для того, чтобы среда времени выполнения поняла, что к ней обращаются, необходимо, чтобы тот, кто обращается, соблюдал соглашения о вызовах. Причем здесь я имею ввиду не только привычные соглашения о вызове, но и всевозможные штуки вроде name mangling и другие штуки, вплоть до знания внутреннего устройства.
Понятно, что на себя эту работу берет компилятор и зачастую программист даже не знает, как там все внутри устроено. Но понимание устройства твоего инструментария, пусть даже несколько утрированное, часто оказывается полезным, особенно в каких-то пограничных или особенно нетривиальных случаях.
Так, в благодаря хорошему пониманию устройства сред выполнения появляются проекты вроде Clasp, объединяющем среды выполнения C++ и Common Lisp.
Иногда среду времени выполнения можно подменить полностью или частично. Так, например, механизм выделения памяти в C можно заменить, скажем, на jemalloc. То же самое касается и других частей среды.
Таким образом, можно показать, что вычислительная часть программы отделена от ее “кирпичиков”, на которые она опирается и рассуждения в подобном ключе иногда полезны.
Текст объемный и рассчитан на:
Примеры процессов выполнения описаны для ОС Windows, но работают по тому же принципу и на других ОС (с учетом различных расширений исполняемых файлов и нативных библиотек).
0. Pay-for-Play
BCL располагается в GAC, откуда приложения загружают необходимые для работы зависимости.
Примеры компонентов, которые поставляются через NuGet:
Этот подход называется «pay-for-play»; другими словами, приложения загружают только ту функциональность, которая им необходима, но каждая такая функциональность содержится в отдельной сборке.
1. FDD vs SCD
В Standalone (SCD)-приложении все компоненты для выполнения (CoreCLR, CoreFX), а также сторонние библиотеки, то есть абсолютно все зависимости, поставляются вместе с самим приложением (чаще всего в одной папке).
Важно понимать, что Standalone-приложение привязано к определенной ОС и архитектуре (например, Windows 7 x64 или OSX 10.12 x64). Такой идентификатор называется Runtime identifier (RID). Для каждой ОС/архитектуры существует своя версия библиотеки Core CLR (и прочих нативных компонентов), поэтому для Standalone-приложений на этапе компиляции в свойстве RuntimeIdentifier нужно указывать параметры целевой системы (RID).
.NET Core Runtime устанавливается в папку C:\Program Files\dotnet:
Файлы фреймворка(-ов) хранятся в папке C:\Program Files\dotnet\shared.
Можно установить несколько версий фреймворка:
Для выполнения Portable-приложения необходимо запустить хост-процесс dotnet.exe и передать ему в качестве аргумента путь к управляемой сборке.
«C:\Program Files\dotnet» добавляется к значению переменной среды PATH, благодаря чему Portable-приложения теперь могут запускаться из командной строки:
В папке приложения (там, где находится [AppName].dll) должен лежать файл [AppName].runtimeconfig.json. В нём указаны имя и версия фреймворка, которые должны быть использованы для выполнения Portable-приложения. Например:
Этот файл является обязательным для Portable-приложений.
Имея вышеприведенную конфигурацию, компоненты среды выполнения будут загружены из папки C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.0.0.
Уменьшение количества файлов объясняется тем, что в Core FX 1.0 отсутствовали многие библиотеки, поэтому они шли в составе приложения, как обычные зависимости. В Core FX 2.0 эти сборки были добавлены, поэтому они больше не поставляются с приложением, а берутся из папки фреймворка.
Наблюдается картина, противоположная Portable-приложениям — чем больше становится Core FX, тем больше файлов поставляется с приложением.
Рекомендации по выбору типа развертывания
5. Runtime Configuration Files
Файлы [AppName].runtimeconfig.json и [AppName].deps.json называют Runtime Configuration Files (*.deps.json называют dependency manifest file). Они создаются в процессе компиляции и содержат всю информацию, необходимую для запуска dotnet.exe и выполнения приложения.
dotnet.exe ([AppName].exe) использует файл [AppName].deps.json для определения абсолютных путей всех зависимостей приложения при его запуске.
Секция targets определяет платформу и дерево зависимостей для нее в формате
[ID зависимости (пакета)]/[версия]: <
dependencies: < список зависимостей (пакетов) данного пакета >,
относительные пути к управляемым и нативным файлам данного пакета
>
Для выполнения любого приложения, target должен обязательно содержать RID, например .NETCoreApp,Version=v1.1/win10-x64. Файл deps.json Standalone-приложения всегда один и содержит RID целевой платформы. Для Portable-приложения файлов deps.json два — один в папке фреймворка, второй в папке приложения. RID для Portable-приложений указан в файле [FrameworkName].deps.json в папке фреймворка. После того, как dotnet.exe определил фреймворк для выполнения приложения, он сперва загружает deps-файл этого фреймворка (например, C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.0.0\Microsoft.NETCore.App.deps), а затем deps-файл приложения. Deps-файл приложения имеет более высокий приоритет.
Рассмотрим подробнее содержимое файла deps.json Standalone-приложения:
В свойстве dependencies перечислены зависимости (пакеты) конкретного пакета.
Свойство runtimeTargets используется в deps-файле Portable-приложения и определяет пути файлов библиотек для конкретного RID. Такие RID-specific библиотеки поставляются вместе с Portable-приложением в папке runtimes.
Свойства runtime и native содержат относительные пути управляемых (managed) и нативных библиотек соответственно. Свойство resources содержит относительные пути и локали локализованных сборок-ресурсов.
Пути относительны к NuGet package cache, а не deps-файлу.
Добавить сторонний deps-файл можно передав значение аргумента —additional-deps или переменную среды DOTNET_ADDITIONAL_DEPS.
Такая возможность доступна только для Portable приложений.
Значение аргумента может содержать полный путь к deps-файлу, а также путь к директории, где расположены общие deps-файлы. Внутри этой директории deps-файлы должны быть расположены в структуре \shared\[FX name]\[FX version]\*.deps. Например, shared\Microsoft.NETCore.App\2.0.3\MyAdditional.deps.json.
Такой подход использует Visual Studio для неявного добавления в проект Application Insights через файл
C:\Program Files\dotnet\additionalDeps\ Microsoft.AspNetCore.ApplicationInsights.HostingStartup\
shared\Microsoft.NETCore.App\ 2.0.3\ Microsoft.AspNetCore.ApplicationInsights.HostingStartup.deps.json
Когда dotnet.exe (MyApp.exe) определяет пути зависимостей приложения, для каждой отдельной библиотеки составляется список из runtime- и native-путей.
6.1. Запуск приложения
выполняется при помощи мультплексора (muxer) из командной строки (одинаково на любой ОС).
6.2. [corehost] Поиск и загрузка Framework Resolver (hostfxr.dll)
На этом этапе dotnet.exe идет в папку [own directory]/host/fxr/. Для Portable-приложений эта библиотека расположена в общей папке C:\Program Files\dotnet\host\fxr\[FXR version]\hostfxr.dll. Если версий будет несколько, dotnet.exe будет всегда использовать последнюю.
После загрузки hostfxr.dll (Framework Resolver) процесс запуска переходит в рамки этой библиотеки.
6.3. [hostfxr] Определение режима выполнения (standalone, muxer, split/FX)
Первая задача hostfxr — определить режим, в котором будет работать хост процесс и таким образом тип приложения — Portable (FDD) или Standalone (SCD). В Portable (FDD)-режиме он также определяет: это запускаемое приложение или команда SDK.
Также для Portable (FDD)-приложения hostfxr определяет фреймворк (.NET Core Runtime), откуда будут загружены компоненты для выполнения.
Алгоритм проверки очень простой — если в папке, откуда был запущен мультиплексор [AppName].exe (в нашем случае dotnet.exe), отсутствует coreclr.dll или [AppName].dll, то приложение Portable. Если один из этих двух файлов существует, то далее идет проверка — приложение Portable (split/FX) или Standalone. Если существует [AppName].dll, то приложение Standalone, иначе — Portable (split/FX).
При запуске в таком режиме можно явно указать пути к файлам конфигурации:
—depsfile
которые будут использованы вместо файлов в папке приложения.
На текущем этапе hostfxr определяет (по данным файла конфигурации), является ли приложение Portable или Standalone.
После загрузки файлов конфигурации и определения режима hostfxr определяет папку фреймворка (.NET Core Runtime).
Для этого hostfxr сначала определит, какие версии установлены в папке shared, а затем выберет из этого списка релиз-версию, с учетом значений в [AppName].runtimeconfig.json.
При выборе версии учитывается параметр Roll Forward On No Candidate Fx, который указывает строгость соответствия заданной версии и имеющихся на машине.
6.5. [hostfxr] Поиск и загрузка hostpolicy.dll
На текущем этапе всё готово для определения путей runtime-компонентов. Этой задачей занимается библиотека hostpolicy.dll, которая называется Host library.
Процесс поиска hostpolicy.dll заключается в последовательных проверках различных локаций. Но сначала определяется версия hostpolicy из deps-файла фреймворка (напр. C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.0.0\Microsoft.NETCore.App.deps). В этом файле будет найден пакет с именем Microsoft.NETCore.DotNetHostPolicy и взята его версия.
Если файл не был найден на предыдущем этапе, hostpolicy.dll будет найдено в папке фреймворка.
Как только опеределена hostpolicy.dll, hostfxr загружает эту библиотеку и передает ей управление.
6.6. [hostpolicy] Определение списка зависимостей
Библиотека hostpolicy.dll отвечает за определение абсолютных путей всех зависимостей приложения.
Прежде всего hostpolicy создаст компонент под названием Dependencies Resolver, который в свою очередь загрузит два deps-файла — файл фреймворка и файл приложения.
Сперва загружается список из deps-файл фреймворка, где будут определены такие зависимости, как CoreCLR и библиотеки CoreFX. Затем список из deps-файла приложения, в котором указаны сборки нашего приложения и их зависимости.
Для каждого deps-файла Dependency Resolver составляет список всех зависимостей для указанной runtimeTarget.
Для каждого пакета сначала составляется список файлов из всех секций runtimeTargets (RID specific зависимости), далее — список всех файлов из секций native и runtime. Такой объединенный список относительных путей всех зависимостей в условном формате
ID пакета — RID — тип asset’а (runtime, native) — пути к файлам называется Target assets.
После того, как были составлены эти два списка файлов зависимостей (RID и не RID), выполняется процесс под названием Reconciling libraries with targets (согласования). Он заключается в том, что для каждого пакета из секции libraries проверяется, существует ли RID specific-файлы, которые должны переопределить обычные.
6.7. [hostpolicy] Определение путей TPA, Core CLR и CLR Jit
Далее Dependency resolver составляет список абсолютных путей файлов управляемых сборок — зависимостей приложения. Этот список называется TPA (Trusted Platform Assemblies) и передается Core CLR для настройки AppDomain. Также составляется список абсолютных путей директорий, в которых находятся остальных файлы зависимостей (кроме coreclr, corejit).
Определение абсолютных путей управляемых сборок происходит путем поиска файлов в Probe paths (путей зондирования). По умолчанию их два — папка фреймворка и папка приложения, и они основаны на расположении deps-файлов. Также можно добавить дополнительные пути:
1) передав аргумент —additionalprobingpath, например
—additionalprobingpath %UserProfile%\\.nuget\\packages
2) указав в файле [AppName].runtimeconfig.json (приоритет ниже, чем у аргумента), например
В папке фреймворка и приложения наличие файла проверятся (при условии, что он был указан в соответствующем deps-файле) без учета относительного пути, в остальных директориях с учетом относительно пути, потому что эти директории рассматриваются как кеш NuGet-пакета.
После составления списка TPA, определяются пути CoreCLR и CLRJit.
При отсутствии deps-файла приложения, dotnet.exe вначале попытается найти эти библиотеки в [app directory]\lib\. При обычном выполнении пути берутся из папки фреймворка (отбросив относительный путь и взяв только имя файла).
Устанавливаются следующие настройки CoreCLR:
Процесс запуска Standalone-приложения отличается от Portable только начальным этапом, а также местоположением компонентов, которые по умолчанию должны располагаться в папке приложения.












