内联函数 的语义很简单: 把函数体复制粘贴到函数调用处 。使用起来也毫无困难,用 inline
关键字修饰函数即可。
那么他的存在是为了干什么呢?
1.inline内联的本质
内联就是把函数体复制粘贴到函数调用处 , 顾名思义,就是把函数里面的代码直接放到调用方,直接上代码
inline fun testA() {
println("I'm a inline")
}
fun run() { testA() }
反编译之后
public final void testA() {
int $i$f$testA = 0;
String var2 = "I'm a inline";
System.out.println(var2);
}
public final void run() {
int $i$f$testA = false;
String var3 = "I'm a inline";
System.out.println(var3);
}
可以看到,run函数没有调用testA函数,而是直接调用的testA里面的代码,那这样有什么作用呢?
这就涉及到了JVM的方法机制
JVM 进行方法调用和方法执行依赖 栈帧,每一个方法从调用开始至执行完成的过程,都对应着一个栈帧在虚拟机栈里从入栈到出栈的过程
线程的栈帧是存储在虚拟机栈中,未内联的情况下,当我们调用run函数时就会产生一个栈帧,在run中在调用testA就会产生两个栈帧。
使用内联之后就会减少调用方法的成本,只产生一个栈帧。
这么方便!那为什么很少有人用呢?
这是因为在java中已经使用了内联,只不过是放到虚拟机里做的,防止开发者的滥用,比如我内联了一个很长的方法,极大的增大字节码长度,反而得不偿失,所以交给虚拟机就行了。
2.kotlin的内联
kotlin中使用内联主要是为了Lambda 使用
fun main() {
println("main")
normal {
println("normal")
return
}
println("main end")
}
inline fun normal(block:() -> Unit) {
block()
}
// 输出
main
normal
-
对于普通的函数,使用内联函数是完全没有必要的,只是减少了一次方法栈的调用,这种优化可以忽略
-
对于带有函数类型参数的高阶函数,我们使用
inline
关键字修饰的内联函数,来节省Lambda
表达式在调用的地方创建匿名类带来的内存开销 -
由于内联函数,不仅可以内联自己内部的代码,还可以内联内部的函数体中的代码(
Lambda
表达式中的代码)。在调用的地方仅仅只是代码的替换,我们可以在Lambda
表达式中,直接使用裸return
来完成最外层函数的返回。这种返回(位于lambda
表达式中,但退出包含它的函数)我们称之为非局部返回。 -
如果需要内联的函数代码逻辑过于复杂,调用该函数又比较频繁,则会导致在编译期间调用该内联函数的地方出现代码臃肿的情况。
3.noinline
noinline 关键字用于标记不应该内联的 lambda 参数。默认情况下,内联函数的所有 lambda 参数都会被内联展开,但有时我们可能希望某些 lambda 参数不被内联。 作用:
- 防止内联:阻止特定的 lambda 参数被内联展开。
- 保留 lambda 参数:适用于需要将 lambda 参数作为对象传递的情况。
既然inline
是一种优化,假设使用者也经过考虑,将函数用inline
修饰,那为什么还会有noinline
这个关键字? 先来思考一个问题:kotlin 中一切都是对象,函数也能作为参数或者返回值,那被内联的函数参数作为参数或者返回值时会怎么样? 答案是不可以,因为被内联的函数已经被展开了,不再是一个对象了,那怎么办?加上noinline
,告诉编译器,这个函数参数不要进行内联。 这里也有一个例外情况,被内联的函数参数,可以作为其他内联函数的参数。为啥?因为被内联函数被展开复制到调用处了哇
inline fun hello(preAction:()->Unit, postAction:()->Unit):()->Unit{
preAction()
println("hello")
postAction()
another(postAction)
/**
* Illegal usage of inline-parameter 'postAction' in 'public inline fun hello(preAction: () -> Unit, postAction: () -> Unit): () -> Unit defined in root package in file Inline.kt'. Add 'noinline' modifier to the parameter declaration
*/
anotherInline(postAction)
return postAction
/**
* Illegal usage of inline-parameter 'postAction' in 'public inline fun hello(preAction: () -> Unit, postAction: () -> Unit): () -> Unit defined in root package in file Inline.kt'. Add 'noinline' modifier to the parameter declaration
*/
}
fun another(action:()->Unit){
action()
}
inline fun anotherInline(action:()->Unit){
action()
}
这里调用another(postAction)
和 return postAction
时,IDE 会报错,提示需要加上noinline
。 也就是说,如果 inline 函数参数中有函数对象,并且这个函数对象需还需要充当其他非 inline 函数的参数或者充当返回值,那么就需要加上noinline
,还有个偷懒的办法,IDE告诉你需要加,那就加上。
作者:Huang兄
链接:juejin.cn/post/738524…
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
转载自作者:Huang兄 链接:juejin.cn/post/738524…