阅读 86

kotlin学习之inline、noinline和crossinline

首先说结论,

inline:通过内联(即函数内容直接插入到调用处)的方式来编译,用于方法

noinline:局部关掉这个优化,来摆脱不能把函数类型的参数当做对象使用的限制,用于参数

crossinline:局部加强内联优化,让内联函数里的函数类型的参数可以间接被调用,代价是不能在Lambda表达式里使用return,用于参数

inline

首先我们定义一个方法,然后在main函数里面调用

fun hello(postAction:() -> Unit){
    println("Hello")
    postAction()
}

fun main(){
    hello{
        println("Bye")
    }
}

那在实际编译的过程是这样的

fun main(){
    val post = object : Function<Unit>{
        override fun invoke(){
            return println("Bye")
        }
    }
    hello(post)
}

可以看到在kotlin中,用一个jvm的对象来作为函数的变量的实际载体,让这个对象去执行实际代码,也就是说生成一个对象去执行Lambda表达式里的代码时,但是这个对象用完就回收了,一两处调用可能并没有什么影响,但是放在一个循环体调用,这样内存占用会一下就飚起来。

这个时候inline就有很大用处了,首先修改一下hello方法,然后在main方法中继续调用

inline fun hello(postAction:() -> Unit){
    println("Hello")
    postAction()
}

fun main(){
    hello{
        println("Bye")
    }
}

这样在实际编译中就会是下面这种形式

fun main(){
    println("Hello")
    println("Bye")
}

这样用内联的方式来减少对象的创建,从而避免性能问题,就不怕在循环体重调用了。

noinline

继续对hello()进行改变。

inline fun hello(preAction:() -> Unit ,noinline postAction:() -> Unit){
    preAction()
    println("Hello")
    postAction()
}
fun main(){
    hello({
        println("Emm...")
    },{
        println("Bye")
    })
}

实际编译的代码大致为

fun main(){
    println("Emm...")
    println("Hello")
    ({
        println("Bye")
    }).invoke()
}

postAction这个参数则不会参与内联了。

运用场景:如果把某个参数作为返回值,因为前面用inline修饰,在实际使用是不会产生一个对象,而是即函数内容直接插入到调用处,那这个时候我们返回,相当于返回一个不存在的对象。这样就有问题了。

crossinline

首先需要知道一条规则:

Lambda表达式不允许使用return,除非这个Lambda表达式是内联函数的参数。
Lambda表达式不允许使用return,除非这个Lambda表达式是内联函数的参数。
Lambda表达式不允许使用return,除非这个Lambda表达式是内联函数的参数。
重要的事情说三遍。

首先来看个例子,

inline fun hello(postAction:() -> Unit){
    println("Hello")
    postAction()
}

fun main(){
    hello{
        println("Bye")
        return
    }
}

这个结果是return掉hello这个方法,还是return掉main这个方法?答案是main(),因为hello是内联函数,所以被编译之后会变成下面这样,

fun main(){
    println("Hello")
    println("Bye")
    return
}

相当于Lambda表达式的return结束的不是直接的外层函数,而是结束直接外层的外层函数。

那我们继续对hello()进行修改如下:

inline fun hello(postAction:() -> Unit){
    println("Hello")
    runOnUiThread{
        postAction()
    }

}

相当于我们对postAction()这个外面有多包裹了一层,那这个时候又是怎样的?

结果是这段代码编译通不过,因为内联函数里的函数类型的参数不允许这种间接调用。如果非要间接调用,那就需要用crossinline修饰这个参数。但是被crossinline修饰的函数类型参数将不再享有lambda表达式使用return的福利,也就是在main的hello()调用处将不能调用return.

inline fun hello(crossinline postAction:() -> Unit){
    println("Hello")
    runOnUiThread{
        postAction()
    }

}

fun main(){
    println("Hello")
    println("Bye")
    //return  不能调用return
}

这样就可以间接调用了。

作者:风月寒

原文链接:https://www.jianshu.com/p/d30139e4e309

文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐