Я знаю, что встроенная функция, возможно, улучшит производительность и вызовет рост сгенерированного кода, но я не уверен, когда ее правильно использовать.
lock(l) { foo() }
Вместо создания объекта функции для параметра и генерации вызова компилятор может выдать следующий код. ( Источник )
l.lock()
try {
foo()
}
finally {
l.unlock()
}
но я обнаружил, что не существует функционального объекта, созданного kotlin для не встроенной функции. Зачем?
/**non-inline function**/
fun lock(lock: Lock, block: () -> Unit) {
lock.lock();
try {
block();
} finally {
lock.unlock();
}
}
function
kotlin
inline-functions
Холи-Ява
источник
источник
inline
функция может улучшить производительность и что это будет заметно для пользователя?Ответы:
Допустим, вы создали функцию более высокого порядка, которая принимает лямбду типа
() -> Unit
(без параметров, без возвращаемого значения) и выполняет ее следующим образом:На языке Java это будет выглядеть примерно так (упрощенно!):
А когда звонишь из Котлина ...
Под капотом здесь
Function
будет создан экземпляр , который обертывает код внутри лямбда (опять же, это упрощено):Таким образом, вызов этой функции и передача ей лямбда всегда создает экземпляр
Function
объекта.С другой стороны, если вы используете
inline
ключевое слово:Когда вы называете это так:
Ни один
Function
экземпляр не будет создан, а код вокруг вызоваblock
внутри встраиваемой функции будет скопирован на место вызова, так что вы получите что - то подобное в байткоде:В этом случае новые экземпляры не создаются.
источник
noinline
иcrossinline
ключевых словах - смотрите документы .Позвольте мне добавить: Когда не использовать
inline
:Если у вас есть простая функция, которая не принимает другие функции в качестве аргумента, не имеет смысла встраивать их. IntelliJ предупредит вас:
Даже если у вас есть функция «с параметрами функциональных типов», вы можете столкнуться с тем, что компилятор сообщит вам, что встраивание не работает. Рассмотрим этот пример:
Этот код не компилируется, что приводит к ошибке:
Причина в том, что компилятор не может встроить этот код, особенно
operation
параметр. Еслиoperation
он не заключен в объект (что было бы результатом примененияinline
), как он вообще может быть назначен переменной? В этом случае компилятор предлагает аргументироватьnoinline
. Не имеет смысла иметьinline
функцию с однойnoinline
функцией, не делайте этого. Однако, если существует несколько параметров функциональных типов, при необходимости рассмотрите возможность встраивания некоторых из них.Итак, вот несколько предлагаемых правил:
noinline
для остальных.reified
параметры типа, которые необходимо использоватьinline
. Прочтите здесь .источник
inline
может быть вредно, - это когда функциональный параметр вызывается несколько раз во встроенной функции, например, в разных условных ветвях. Я только что столкнулся со случаем, когда из-за этого дублировался весь байт-код для функциональных аргументов.crossinline
параметры?Самый важный случай, когда мы используем модификатор inline, - это когда мы определяем утилитарные функции с функциями параметров. Коллекция или обработка строк (например
filter
,map
илиjoinToString
) или просто автономные функции являются прекрасным примером.Вот почему встроенный модификатор в основном является важной оптимизацией для разработчиков библиотек. Они должны знать, как это работает, каковы его улучшения и затраты. Мы должны использовать модификатор inline в наших проектах, когда мы определяем наши собственные служебные функции с параметрами типа функции.
Если у нас нет параметра типа функции, параметра повторного типа и нам не нужен нелокальный возврат, то, скорее всего, нам не следует использовать встроенный модификатор. Вот почему у нас будет предупреждение в Android Studio или IDEA IntelliJ.
Кроме того, существует проблема с размером кода. Встраивание большой функции может значительно увеличить размер байт-кода, поскольку он копируется на каждый сайт вызова. В таких случаях вы можете выполнить рефакторинг функции и извлечь код в обычные функции.
источник
Функции высшего порядка очень полезны и действительно могут улучшить
reusability
код. Однако одна из самых больших проблем при их использовании - это эффективность. Лямбда-выражения компилируются в классы (часто анонимные классы), а создание объектов в Java - сложная операция. Мы по-прежнему можем эффективно использовать функции высшего порядка, сохраняя при этом все преимущества, сделав функции встроенными.вот встроенная функция в картинку
Если функция помечена как
inline
, во время компиляции кода компилятор заменит все вызовы функции фактическим телом функции. Кроме того, лямбда-выражения, предоставленные в качестве аргументов, заменяются их фактическим телом. Они будут рассматриваться не как функции, а как реальный код.Вкратце: - Inline -> вместо того, чтобы быть вызванными, они заменяются кодом тела функции во время компиляции ...
В Kotlin использование функции в качестве параметра другой функции (так называемые функции высшего порядка) кажется более естественным, чем в Java.
Однако использование лямбда-выражений имеет некоторые недостатки. Поскольку это анонимные классы (и, следовательно, объекты), им нужна память (и они могут даже увеличивать общее количество методов вашего приложения). Чтобы этого избежать, мы можем встроить наши методы.
Из приведенного выше примера : - Эти две функции делают одно и то же - выводят результат функции getString. Один встроен, а другой нет.
Если вы проверите декомпилированный java-код, вы увидите, что методы полностью идентичны. Это потому, что ключевое слово inline - это инструкция компилятору скопировать код на сайт вызова.
Однако, если мы передаем любой тип функции другой функции, как показано ниже:
Чтобы решить эту проблему, мы можем переписать нашу функцию, как показано ниже:
Предположим, у нас есть функция более высокого порядка, как показано ниже:
Здесь компилятор скажет нам не использовать ключевое слово inline, когда есть только один лямбда-параметр, и мы передаем его другой функции. Итак, мы можем переписать вышеуказанную функцию, как показано ниже:
Примечание : нам также пришлось удалить ключевое слово noinline, потому что оно может использоваться только для встроенных функций!
Предположим, у нас есть такая функция ->
Это работает нормально, но основа логики функции загрязнена кодом измерения, что затрудняет вашим коллегам работу над тем, что происходит.:)
Вот как встроенная функция может помочь этому коду:
Теперь я могу сконцентрироваться на чтении основного предназначения функции intercept (), не пропуская строки кода измерения. Нам также выгодна возможность повторного использования этого кода в других местах, где мы хотим
inline позволяет вызывать функцию с лямбда-аргументом внутри замыкания ({...}) вместо передачи лямбда-подобной меры (myLamda)
Когда это полезно?
Ключевое слово inline полезно для функций, которые принимают другие функции или лямбда-выражения в качестве аргументов.
Без ключевого слова inline в функции лямбда-аргумент этой функции преобразуется во время компиляции в экземпляр интерфейса функции с помощью одного метода invoke (), а код в лямбда-выражении выполняется путем вызова invoke () для этого экземпляра функции. внутри тела функции.
С ключевым словом inline в функции такое преобразование времени компиляции никогда не происходит. Вместо этого тело встроенной функции вставляется в ее сайт вызова, и ее код выполняется без накладных расходов на создание экземпляра функции.
Хммм? Пример в android ->
Допустим, у нас есть функция в классе маршрутизатора активности для запуска действия и применения некоторых дополнительных функций.
Эта функция создает намерение, применяет некоторые дополнения, вызывая аргумент функции applyExtras, и запускает действие.
Если мы посмотрим на скомпилированный байт-код и декомпилируем его в Java, это будет выглядеть примерно так:
Допустим, мы вызываем это из прослушивателя кликов в действии:
Декомпилированный байт-код для этого прослушивателя кликов будет выглядеть примерно так:
Новый экземпляр Function1 создается каждый раз, когда запускается прослушиватель кликов. Это нормально работает, но не идеально!
Теперь давайте просто добавим inline к нашему методу маршрутизатора активности:
Не меняя вообще код прослушивателя кликов, теперь мы можем избежать создания этого экземпляра Function1. Эквивалент Java-кода прослушивателя кликов теперь будет выглядеть примерно так:
Это оно.. :)
«Встроить» функцию в основном означает скопировать тело функции и вставить его в место вызова функции. Это происходит во время компиляции.
источник
inline functions
. Можете ли вы дать реальный пример использования (например, в реальном проекте, над которым вы работали)?Один простой случай, когда он может вам понадобиться, - это когда вы создаете служебную функцию, которая принимает блок приостановки. Учти это.
В этом случае наш таймер не будет принимать функции приостановки. Чтобы решить эту проблему, у вас может возникнуть соблазн приостановить ее также
Но тогда его можно использовать только из самих сопрограмм / приостановленных функций. Тогда вы в конечном итоге сделаете асинхронную версию и неасинхронную версию этих утилит. Проблема исчезнет, если вы сделаете это встроенным.
Вот детская площадка котлина с состоянием ошибки. Сделайте таймер встроенным, чтобы решить эту проблему.
источник
inline fun
. Не могли бы вы уточнить?