首先說結(jié)論,
inline:通過內(nèi)聯(lián)(即函數(shù)內(nèi)容直接插入到調(diào)用處)的方式來編譯,用于方法
noinline:局部關(guān)掉這個優(yōu)化,來擺脫不能把函數(shù)類型的參數(shù)當做對象使用的限制,用于參數(shù)
crossinline:局部加強內(nèi)聯(lián)優(yōu)化,讓內(nèi)聯(lián)函數(shù)里的函數(shù)類型的參數(shù)可以間接被調(diào)用,代價是不能在Lambda表達式里使用return,用于參數(shù)
inline
首先我們定義一個方法,然后在main函數(shù)里面調(diào)用
fun hello(postAction:() -> Unit){
println("Hello")
postAction()
}
fun main(){
hello{
println("Bye")
}
}
那在實際編譯的過程是這樣的
fun main(){
val post = object : Function<Unit>{
override fun invoke(){
return println("Bye")
}
}
hello(post)
}
可以看到在kotlin中,用一個jvm的對象來作為函數(shù)的變量的實際載體,讓這個對象去執(zhí)行實際代碼,也就是說生成一個對象去執(zhí)行Lambda表達式里的代碼時,但是這個對象用完就回收了,一兩處調(diào)用可能并沒有什么影響,但是放在一個循環(huán)體調(diào)用,這樣內(nèi)存占用會一下就飚起來。
這個時候inline就有很大用處了,首先修改一下hello方法,然后在main方法中繼續(xù)調(diào)用
inline fun hello(postAction:() -> Unit){
println("Hello")
postAction()
}
fun main(){
hello{
println("Bye")
}
}
這樣在實際編譯中就會是下面這種形式
fun main(){
println("Hello")
println("Bye")
}
這樣用內(nèi)聯(lián)的方式來減少對象的創(chuàng)建,從而避免性能問題,就不怕在循環(huán)體重調(diào)用了。
noinline
繼續(xù)對hello()進行改變。
inline fun hello(preAction:() -> Unit ,noinline postAction:() -> Unit){
preAction()
println("Hello")
postAction()
}
fun main(){
hello({
println("Emm...")
},{
println("Bye")
})
}
實際編譯的代碼大致為
fun main(){
println("Emm...")
println("Hello")
({
println("Bye")
}).invoke()
}
postAction這個參數(shù)則不會參與內(nèi)聯(lián)了。
運用場景:如果把某個參數(shù)作為返回值,因為前面用inline修飾,在實際使用是不會產(chǎn)生一個對象,而是即函數(shù)內(nèi)容直接插入到調(diào)用處,那這個時候我們返回,相當于返回一個不存在的對象。這樣就有問題了。
crossinline
首先需要知道一條規(guī)則:
Lambda表達式不允許使用return,除非這個Lambda表達式是內(nèi)聯(lián)函數(shù)的參數(shù)。
Lambda表達式不允許使用return,除非這個Lambda表達式是內(nèi)聯(lián)函數(shù)的參數(shù)。
Lambda表達式不允許使用return,除非這個Lambda表達式是內(nèi)聯(lián)函數(shù)的參數(shù)。
重要的事情說三遍。
首先來看個例子,
inline fun hello(postAction:() -> Unit){
println("Hello")
postAction()
}
fun main(){
hello{
println("Bye")
return
}
}
這個結(jié)果是return掉hello這個方法,還是return掉main這個方法?答案是main(),因為hello是內(nèi)聯(lián)函數(shù),所以被編譯之后會變成下面這樣,
fun main(){
println("Hello")
println("Bye")
return
}
相當于Lambda表達式的return結(jié)束的不是直接的外層函數(shù),而是結(jié)束直接外層的外層函數(shù)。
那我們繼續(xù)對hello()進行修改如下:
inline fun hello(postAction:() -> Unit){
println("Hello")
runOnUiThread{
postAction()
}
}
相當于我們對postAction()這個外面有多包裹了一層,那這個時候又是怎樣的?
結(jié)果是這段代碼編譯通不過,因為內(nèi)聯(lián)函數(shù)里的函數(shù)類型的參數(shù)不允許這種間接調(diào)用。如果非要間接調(diào)用,那就需要用crossinline修飾這個參數(shù)。但是被crossinline修飾的函數(shù)類型參數(shù)將不再享有l(wèi)ambda表達式使用return的福利,也就是在main的hello()調(diào)用處將不能調(diào)用return.
inline fun hello(crossinline postAction:() -> Unit){
println("Hello")
runOnUiThread{
postAction()
}
}
fun main(){
println("Hello")
println("Bye")
//return 不能調(diào)用return
}
這樣就可以間接調(diào)用了。