Kotlin 三個inline

整理借鑒出自 https://mp.weixin.qq.com/s/iOId4kwYe1HCyrS4T-yb8w
做筆記用, 一直理解比較混亂,現(xiàn)在算是清晰了,為了防止找不到這個講解

inline

Kotlin 里有個特別好用的關鍵字叫 inline,它可以幫你對做了標記的函數(shù)進行內(nèi)聯(lián)優(yōu)化。所謂內(nèi)聯(lián)就是,調(diào)用的函數(shù)在編譯的時候會變成代碼內(nèi)嵌的形式:

-> 正常寫是這樣
fun main() {
    hello()
}
inline fun hello(){
    println("hello")
}
-> 編譯后的代碼
fun main() {
   println("hello")
}

編譯時常量

kotlin 聲明 const val AUTHOR = "David"
查看對應的java 代碼->
public static final String AUTHOR = "David";

Java 里有個概念叫編譯時常量 Compile-time Constant,直觀地講就是這個變量的值是固定不變的,并且編譯器在編譯的時候就能確定這個變量的值。具體到代碼上,就是這個變量需要是 final 的,類型只能是字符串或者基本類型,而且這個變量需要在聲明的時候就賦值.
這種編譯時常量,會被編譯器以內(nèi)聯(lián)的形式進行編譯,也就是直接把你的值拿過去替換掉調(diào)用處的變量名來編譯。這樣一來,程序結構就變簡單了,編譯器和 JVM 也方便做各種優(yōu)化。這,就是編譯時常量的作用。

這種編譯時常量,到了 Kotlin 里有了一個專有的關鍵字,叫 const:一個變量如果以 const val 開頭,它就會被編譯器當做編譯時常量來進行內(nèi)聯(lián)式編譯

讓變量內(nèi)聯(lián)用的是 const;而除了變量,Kotlin 還增加了對函數(shù)進行內(nèi)聯(lián)的支持。在 Kotlin 里,你給一個函數(shù)加上 inline 關鍵字,這個函數(shù)就會被以內(nèi)聯(lián)的方式進行編譯。

事實上,inline 關鍵字不止可以內(nèi)聯(lián)自己的內(nèi)部代碼,還可以內(nèi)聯(lián)自己內(nèi)部的內(nèi)部的代碼。什么叫「內(nèi)部的內(nèi)部」?就是自己的函數(shù)類型的參數(shù)。

fun hello(postAction:()->Unit){
    println("hello")
    postAction
}

fun main() {
    hello {
        println("bye")
    }
}

因為 Java 并沒有對函數(shù)類型的變量的原生支持,Kotlin 需要想辦法來讓這種自己新引入的概念在 JVM 中落地。而它想的辦法是什么呢?就是用一個 JVM 對象來作為函數(shù)類型的變量的實際載體,讓這個對象去執(zhí)行實際的代碼。也就是說,在我對代碼做了剛才那種修改之后,程序在每次調(diào)用 hello() 的時候都會創(chuàng)建一個對象來執(zhí)行 Lambda 表達式里的代碼,雖然這個對象是用一下之后馬上就被拋棄,但它確實被創(chuàng)建了。

這有什么壞處?其實一般情況下也沒什么壞處,多創(chuàng)建個對象算什么?但是你想一下,如果這種函數(shù)被放在循環(huán)里執(zhí)行:

fun main() {
    for (i in 1.. 100){
        hello {
            println("bye")
        }
    }
}

內(nèi)存占用是不是一下就飚起來了?而且關鍵是,你作為函數(shù)的創(chuàng)建者,并不知道、也沒法規(guī)定別人在什么地方調(diào)用這個函數(shù),也就是說,這個函數(shù)是否出現(xiàn)在循環(huán)或者界面刷新之類的高頻場景里,是完全不可控的。

如果我們加上inline 編譯后就不是這樣

inline fun hello(postAction:()->Unit){
    println("hello")
    postAction()
}

mian -> 會變成這樣

fun main() {
    for (i in 1.. 100){
           println("hello")
            println("bye")
       
    }
}

相當于吧嵌套去掉,展評了,減少棧操作和對象創(chuàng)建

noinline

說完 inline,我們來說另一個關鍵字:noinline。noinline 的意思很直白:inline 是內(nèi)聯(lián),而 noinline 就是不內(nèi)聯(lián)。不過它不是作用于函數(shù)的,而是作用于函數(shù)的參數(shù):對于一個標記了 inline 的內(nèi)聯(lián)函數(shù),你可以對它的任何一個或多個函數(shù)類型的參數(shù)添加 noinline 關鍵字:

inline fun hello(preAction: () -> Unit, noinline postAction: () -> Unit) {
    preAction()
    println("hello")
    postAction()
}

//添加了之后,這個參數(shù)就不會參與內(nèi)聯(lián)了:
fun main() {
    hello({
        println("一言難盡")
    }, {
        println("Bye!")
    })
}

// 實際編譯
public static final void main() {
      
        println("一言難盡")
        ->inline
        println("hello")
        -> noinline
         ( {
        println("Bye!")
        }).invoke()
   }

如果我們在函數(shù)末尾添加返回值return postAction

-> postAction 需要加noinline 
inline fun hello(preAction: () -> Unit, postAction: () -> Unit):()->Unit {
    preAction()
    println("hello")
    postAction()
-> 這里會報錯
    return postAction
}

原因,如果不加noinline 那么展開后是這個鬼樣子

// 實際編譯
public static final void main() {
     
        println("一言難盡")
        println("hello")
        println("Bye!")
        -> 這個時候沒人認得這個東西是啥
       postAction
   }

所以,noinline 的作用是什么?是用來局部地、指向性地關掉函數(shù)的內(nèi)聯(lián)優(yōu)化的。既然是優(yōu)化,為什么要關掉?因為這種優(yōu)化會導致函數(shù)中的函數(shù)類型的參數(shù)無法被當做對象使用,也就是說,這種優(yōu)化會對 Kotlin 的功能做出一定程度的收窄。而當你需要這個功能的時候,就要手動關閉優(yōu)化了。這也是 inline 默認是關閉、需要手動開啟的另一個原因:它會收窄 Kotlin 的功能。

那么,我們應該怎么判斷什么時候用 noinline 呢?很簡單,比 inline 還要簡單:你不用判斷,Android Studio 會告訴你的。當你在內(nèi)聯(lián)函數(shù)里對函數(shù)類型的參數(shù)使用了風騷操作,Android Studio 拒絕編譯的時候,你再加上 noinline 就可以了。

crossinline

具體細節(jié)看文章吧,弄碼哥nomag, http://www.itdecent.cn/p/cd0be9b887ec

inline 關鍵字的作用,是把 inline 方法以及方法中的 lambda 參數(shù)在編譯期間復制到調(diào)用方,進而減少函數(shù)調(diào)用以及對象生成。對于有時候我們不想讓 inline 關鍵字對 lambda 參數(shù)產(chǎn)生影響,可以使用 noline 關鍵字。如果想 lambda 也被 inline,但是不影響調(diào)用方的控制流程,那么就要是用 crossinline。

兩篇文章結合看,相信完全可以看懂是啥了。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容