整理借鑒出自 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。
兩篇文章結合看,相信完全可以看懂是啥了。