我们知道在函数在编译器中都有自己的内存地址,我们在运行程序时每遇到一个函数调用都会进行:地址跳转->执行->返回。这种转移操作要求在转去前要保护现场并记忆执行的地址,转回后先要恢复现场,并按原来保存地址继续执行。也就是通常说的压栈和出栈。因此,函数调用要有一定的时间和空间方面的开销。对于那些函数体代码不是很大,又频繁调用的函数来说,这个时间和空间的消耗会很大。
内联函数就是为了解决这个问题而出现。所谓内联函数就是在程序编译时,编译器将程序中出现的内联函数的表达式整合到函数被调用的地方去,从而生成的代码内存地址是连续的,减少了压栈/出栈的时间。实际就是用空间换时间的一个策略。
inline 内联
如果上面的解释没看懂的话,看代码:
fun lock<T>(lock: Lock, body: () -> T): T {
...
body()
...
}
这个函数在编译时会出现两个代码段(也就是两个内存地址):
1.lock函数 : 0xXXXXXXXX;
2.body的实际函数:0xYYYYYYYYY;
如果使用内联
inline fun lock<T>(lock: Lock, body: () -> T): T {
...
body()
...
}
则lock函数中调用body()的地方会被body函数体代替,最后只生成了一个连续的内存地址
lock函数 : 0xZZZZZZZZ;
noinline 禁用内联
如果一个内联函数的参数中有多个 Lambda 表达式, 而你只希望内联其中的一部分, 你可以对函数的一部分参数添加 noinline 标记:
inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) {
// ...
}
这样的话,lambda:inlined是内联,lambda:notInlined就是非内联
非局部返回
在上一章中的限定的返回中我们只是简单的介绍了如何使用,这里就仔细的说一下。
kotlin中使用无限定符的return语句只能用来退出一个普通函数或者匿名函数(也就是fun
定义的函数),如果我们想要退出lambda表达式必须使用一个标签。
继续使用上一章中的lock函数:
fun<T> lock(body: ()->T): T{
val lock = Any()
synchronized(lock){
return body()
}
}
inline fun<T> inlineLock(body: ()->T): T{
val lock = Any()
synchronized(lock){
return body()
}
}
inlineLock
的lambda中这种返回就被成为局部返回。他会直接跳出整个inlineLock函数。比如我们在循环中使用的return就是这种。
for (item in list){
if (item==2){
return
}
}
crossinline
一些内联函数可能调用传给它们的不是直接来自函数体、而是来自另一个执行 上下文的 lambda 表达式参数,例如来自局部对象或嵌套函数。在这种情况下,该 lambda 表达式中 也不允许非局部控制流。为了标识这种情况,该 lambda 表达式参数需要 用 crossinline 修饰符标记:
inline fun f(crossinline body: () -> Unit) {
val f = object: Runnable {
override fun run() = body()
}
// ……
}