Kotlin内联函数关键字inline、noinline和crossinline
你在网上查这个东西,得到的结果肯定是这样:
inline: 函数的代码内联到调用处noinline:inline函数的形参中,不希望内联的lambda,此时这个lambda里不能returncrossinline:inline函数的形参中的lambda不能有return
你这乍一看,noinline和crossinline好像根本没毛线区别
但事实不是这样的
inline做了些啥⌗
inline可以将代码块内联,同时导致一个结果,你可以在这个内联的代码块内部,直接返回调用此代码块的函数,例如:
inline fun testInline(block: () -> Unit) {
    block()
    println("Hey! Don't pass me!")
}
fun main() {
    testInline {
        println("I'm the evil block")
        // This will pass the println() call in testInline()
        return
    }
}
// 上面的代码内联后:
fun main() {
    println("I'm the evil block")
    return
    // Cannot reach this line
    println("Hey! Don't pass me!")
}
这导致了一个问题,在block()执行后,可能无法完成inline函数中的剩余过程,这在很多Builder模式的lambda中是非常致命的,此时,就需要禁用这种非局部返回,就需要使用到crossinline
crossinline⌗
使用crossinline修饰lambda,就可以阻止lambda内部的return,return只可以返回到lambda的作用域内
inline fun testInline(crossinline block: () -> Unit) {
    block()
    println("You can't pass me again!")
}
fun main() {
    testInline {
        println("HAHAHAHAHAHA!")
        // 'return' is not allowed here
        return
        // return@testInline is allowed
    }
}
noinline⌗
再说说noinline,直译这个关键字也能看得挺明白了,就是不让你inline,用原来的方式传递lambda参数。
那什么场景下需要noinline呢?一般就是你需要一个lambda的引用的时候,例如:
class Timer(
    val interval: Long,
    val callback: () -> Unit
) { 
    /* other logic that may call tick() */
    
    /* invoke callback */
    fun tick() = callback()
}
inline fun makeTimer(
    interval: () -> Long,
    noinline callback: () -> Unit
) = Timer(interval(), callback)
fun main() {
    val timer = makeTimer(
        interval = { 1000L },
        callback = { println("1 seconds passed.") }
    )
}
在makerTimer中的callback参数前,你必须添加noinline关键字,因为Timer需要持有callback这个lambda的引用才能在合适的时机调用它,自然也就不能把它做成内联的了。
而interval这个参数只有调用makeTimer时执行一次,自然就可以内联。
但是要注意,虽然noinline也可以实现crossinline的效果,但是与crossinline有所不同
如果使用noinline,对lambda表达式的调用会转换成普通的lambda调用,即生成Function实例,然后传递实例,并调用实例的invoke()方法来运行lambda,这样自然也就禁用了非局部返回,因为此时已经在另一个函数的域内了,return根本不知道返回到哪。
总结⌗
绝大多数情况下,使用noinline引起的不能局部返回是结果,而使用crossinline禁用局部返回是目的。