欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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在高阶函数实现上的缺陷(对象创建)