首先说结论,
inline:通过内联(即函数内容直接插入到调用处)的方式来编译,用于方法
noinline:局部关掉这个优化,来摆脱不能把函数类型的参数当做对象使用的限制,用于参数
crossinline:局部加强内联优化,让内联函数里的函数类型的参数可以间接被调用,代价是不能在Lambda表达式里使用return,用于参数
inline
首先我们定义一个方法,然后在main函数里面调用
fun hello(postAction:() -> Unit){
println("Hello")
postAction()
}
fun main(){
hello{
println("Bye")
}
}
那在实际编译的过程是这样的
fun main(){
val post = object : Function{
override fun invoke(){
return println("Bye")
}
}
hello(post)
}
可以看到在kotlin中,用一个jvm的对象来作为函数的变量的实际载体,让这个对象去执行实际代码,也就是说生成一个对象去执行Lambda表达式里的代码时,但是这个对象用完就回收了,一两处调用可能并没有什么影响,但是放在一个循环体调用,这样内存占用会一下就飚起来。
这个时候inline就有很大用处了,首先修改一下hello方法,然后在main方法中继续调用
inline fun hello(postAction:() -> Unit){
println("Hello")
postAction()
}
fun main(){
hello{
println("Bye")
}
}
这样在实际编译中就会是下面这种形式
fun main(){
println("Hello")
println("Bye")
}
这样用内联的方式来减少对象的创建,从而避免性能问题,就不怕在循环体重调用了。
noinline
继续对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这个参数则不会参与内联了。
运用场景:如果把某个参数作为返回值,因为前面用inline修饰,在实际使用是不会产生一个对象,而是即函数内容直接插入到调用处,那这个时候我们返回,相当于返回一个不存在的对象。这样就有问题了。
crossinline
首先需要知道一条规则:
Lambda表达式不允许使用return,除非这个Lambda表达式是内联函数的参数。
Lambda表达式不允许使用return,除非这个Lambda表达式是内联函数的参数。
Lambda表达式不允许使用return,除非这个Lambda表达式是内联函数的参数。
重要的事情说三遍。
首先来看个例子,
inline fun hello(postAction:() -> Unit){
println("Hello")
postAction()
}
fun main(){
hello{
println("Bye")
return
}
}
这个结果是return掉hello这个方法,还是return掉main这个方法?答案是main(),因为hello是内联函数,所以被编译之后会变成下面这样,
fun main(){
println("Hello")
println("Bye")
return
}
相当于Lambda表达式的return结束的不是直接的外层函数,而是结束直接外层的外层函数。
那我们继续对hello()进行修改如下:
inline fun hello(postAction:() -> Unit){
println("Hello")
runOnUiThread{
postAction()
}
}
相当于我们对postAction()这个外面有多包裹了一层,那这个时候又是怎样的?
结果是这段代码编译通不过,因为内联函数里的函数类型的参数不允许这种间接调用。如果非要间接调用,那就需要用crossinline修饰这个参数。但是被crossinline修饰的函数类型参数将不再享有lambda表达式使用return的福利,也就是在main的hello()调用处将不能调用return.
inline fun hello(crossinline postAction:() -> Unit){
println("Hello")
runOnUiThread{
postAction()
}
}
fun main(){
println("Hello")
println("Bye")
//return 不能调用return
}
这样就可以间接调用了。