Kotlin中的inline作用
程序员文章站
2024-03-20 23:02:28
...
1.inline的作用
1.1 可以将函数体直接复制到函数调用处
package com.example.kotlinsyntax._inline
class InlineClass {
//inline标记的函数
inline fun doInline() {
print("inline")
}
//普通函数
fun doNoInline() {
print("noInline")
}
//调用处函数
fun test() {
doInline()
doNoInline()
}
}
上面是测试用的原函数,分别是一个inline函数,一个普通,一个负责调用这2个函数的测试函数,看下编译的字节码
package com.example.kotlinsyntax._inline;
import kotlin.Metadata;
@Metadata(
mv = {1, 1, 16},
bv = {1, 0, 3},
k = 1,
d1 = {"\u0000\u0014\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0010\u0002\n\u0002\b\u0003\u0018\u00002\u00020\u0001B\u0005¢\u0006\u0002\u0010\u0002J\t\u0010\u0003\u001a\u00020\u0004H\u0086\bJ\u0006\u0010\u0005\u001a\u00020\u0004J\u0006\u0010\u0006\u001a\u00020\u0004¨\u0006\u0007"},
d2 = {"Lcom/example/kotlinsyntax/_inline/InlineClass;", "", "()V", "doInline", "", "doNoInline", "test", "kotlinsyntax"}
)
public final class InlineClass {
public final void doInline() {
int $i$f$doInline = 0;
String var2 = "inline";
boolean var3 = false;
System.out.print(var2);
}
public final void doNoInline() {
String var1 = "noInline";
boolean var2 = false;
System.out.print(var1);
}
/**
* 函数调用处
*/
public final void test() {
int $i$f$doInline = false; // 1
String var3 = "inline"; // 2
boolean var4 = false; // 3
System.out.print(var3); // 4
this.doNoInline(); // 5
}
}
可以看到test函数内部1~4和doInline函数体完全一致,证实了函数体被直接复制到了调用处,5处是doNoInline的调用,可以发现没有讲函数体复制过去
这样可以减少一个调用栈的产生,但是这种优化及其微小,而且如果inline函数体很大,调用他的地方有很多,这样还会产生更多的字节码,使最终产生的文件变大,那他真正的作用在哪里呢?
1.2 inline优化高阶函数调用
如果一个函数包含一个函数类型参数,比如
//参数func 是一个函数类型
fun advancedFunction(func: () -> Unit) {
func()
}
kotlin在实现这个函数调用实际是为这个func函数创建一个函数对象,然后调用这个函数对象的invoke方法
public final class InlineClass {
public final void doInline() {
int $i$f$doInline = 0;
String var2 = "inline";
boolean var3 = false;
System.out.print(var2);
}
public final void doNoInline() {
String var1 = "noInline";
boolean var2 = false;
System.out.print(var1);
}
public final void advancedFunction(@NotNull Function0 func) {
Intrinsics.checkParameterIsNotNull(func, "func");
func.invoke();
}
public final void test() {
int $i$f$doInline = false;
String var3 = "inline";
boolean var4 = false;
System.out.print(var3);
this.doNoInline();
this.advancedFunction((Function0)null.INSTANCE);
}
}
可以看到生成的字节码,jvm为我们创建了Function0函数对象,最终调用func.invoke实现原函数调用。
如果advancedFunction函数是在循环中被调用呢?
while(true) {
advancedFunction({ xxx })
}
那就意味着这时候会频繁创建新的函数对象,我们在自定义View的时候都知道要避免的onMeasure和onDraw中创建对象,这样有可能造成内存抖动。如何避免这种情况发生呢,inline就可以发挥作用了
inline函数可以将自身和自身的函数参数都内联化,也就是将自身和自身的函数参数都复制到调用处,我们先讲上面的函数改为内联
class InlineClass {
inline fun doInline() {
print("inline")
}
fun doNoInline() {
print("noInline")
}
inline fun advancedFunction(func: () -> Unit) {
print("advanced")
func()
}
fun test() {
doInline()
doNoInline()
advancedFunction { print("func") }
}
}
查看生成的字节码
//去除了doInline和doNoInline的代码
public final class InlineClass {
public final void advancedFunction(@NotNull Function0 func) {
int $i$f$advancedFunction = 0;
Intrinsics.checkParameterIsNotNull(func, "func");
String var3 = "advanced";
boolean var4 = false;
System.out.print(var3);
func.invoke();
}
public final void test() {
int $i$f$advancedFunction = false;
$i$f$advancedFunction = false;
var3 = "advanced"; // 1
var4 = false; // 2
System.out.print(var3); // 3
int var5 = false; // 4
String var6 = "func"; // 5
boolean var7 = false; // 6
System.out.print(var6); // 7
}
}
可以看到1~3和advancedFunction原函数代码一致
5~7并不再是之前的this.advancedFunction((Function0)null.INSTANCE);
而是将func函数内部的print函数代码复制了过来,这样就有效避免了重复创建对象,inline对这种情况进行了极大的优化
小结:inline可以弥补kotlin在高阶函数实现上的缺陷(对象创建)