1.此代码编译时出现警告(* 对性能的影响微不足道 *):
inline fun test(noinline f: () -> Unit) {
thread(block = f)
}
1.此代码未编译(* 非法使用内联参数 *):
inline fun test(crossinline f: () -> Unit) {
thread(block = f)
}
1.此代码编译时出现警告(* 对性能的影响微不足道 *):
inline fun test(noinline f: () -> Unit) {
thread { f() }
}
1.此代码编译时没有警告或错误:
inline fun test(crossinline f: () -> Unit) {
thread { f() }
}
以下是我的问题:
- 为什么(2)不能编译,而(4)可以?
noinline
和crossinline
之间到底有什么区别?- 如果(3)没有产生性能改进,为什么(4)会产生性能改进?
4条答案
按热度按时间z18hc3ub1#
从inline functions reference:
请注意,一些内联函数可能不直接从函数体调用作为参数传递给它们的lambda,而是从另一个执行上下文,如本地对象或嵌套函数调用。在这种情况下,lambda中也不允许非本地控制流。为了指示这一点,lambda参数需要用交叉修饰符标记
因此,示例2.没有编译,因为
crossinline
只强制执行局部控制流,而表达式block = f
违反了这一点。示例1编译,因为noinline
不需要这样的行为(显然,因为它是一个普通的函数参数)。示例1和3没有产生任何性能改进,因为唯一的lambda参数被标记为
noinline
,使得函数的inline
修饰符无用且多余-编译器想要内联一些东西,但所有可以内联的东西都被标记为不内联。考虑两个函数A和B
A
B
函数A的行为类似于函数B,因为参数
f
不会被内联(B函数不会内联test
的主体,而在A函数中,主体:thread { f() }
仍然被内联)。但是,在示例4中,情况并非如此,因为
crossinline f: () -> Unit
参数**可以内联,所以它不能违反前面提到的非本地控制流规则(比如给全局变量赋值),如果可以内联,编译器就认为性能得到了提高,并且不会像示例3那样发出警告。jtjikinw2#
让我试着用例子来解释这一点:我将逐一介绍您的每个示例,并描述它命令编译器执行的操作。首先,下面是一些使用您的函数的代码:
现在让我们来看看你的变体,我将调用例子中的函数
test1
..test4
,我将用伪代码展示上面的main
函数编译成什么。1.一米三英寸一英寸,一米四英寸一英寸
test1()
编译时出现警告(对性能的影响微不足道)*首先,注意在
compiledMain1
中,甚至没有inline fun test1
存在的证据,内联函数并不是真正的“调用”:就好像test1
的代码是写在main()
里面的一样,另一方面,noinline
lambda参数的行为与没有内联时相同:创建一个lambda对象并将其传递给thread
函数。2.一米十二分一秒,一米十三分一秒
test2()
未编译(非法使用内联参数)*我希望我能想象出这里发生了什么:您请求将块的代码复制粘贴到需要值的位置。这只是语法垃圾。原因:不管有没有
crossinline
,你都要求将块复制粘贴到它被使用的地方。这个修饰符只是限制你可以在块中写入什么(没有return
等)。3. x一米十七英寸x一米十八英寸
test3()
编译时出现警告(性能影响微不足道)*现在我们回到
noinline
,事情又变得简单了,你创建一个常规的lambda对象myBlock
,然后创建另一个委托给它的常规lambda对象:{ myBlock() }
,然后将其传递给thread()
。4.一米二十四英寸,一米二十五英寸
test4()
编译时没有警告或错误 *最后这个例子演示了
crossinline
的作用。test4
的代码被内联到main
中,块的代码被内联到它被使用的地方。但是,由于它是在一个常规lambda对象的定义中使用的,所以它不能包含非本地控制流。关于性能影响
Kotlin团队希望你合理地使用内联特性。使用内联,编译代码的大小会急剧膨胀,甚至达到JVM的限制,每个方法最多64K字节码指令。主要的用例是高阶函数,它们避免了创建实际lambda对象的成本,只是在一个函数调用之后立即丢弃它。
当你声明一个没有任何内联lambda的
inline fun
时,内联本身就失去了作用,编译器会警告你。vfhzx4xs3#
Q1:为什么(2)不能编译,而(4)可以?
来自他们的文档:
可内联的lambda只能在内联函数内部调用或作为可内联参数传递...
答:
方法
thread(...)
不是inline
方法,因此不能将f
作为参数传递。Q2:无线和交叉线的区别到底是什么?
答:
noinline
会阻止lambda的内联,当你有多个lambda参数,并且只想内联传递给内联函数的一部分lambda时,这会很有用。crossinline
用于标记不允许非本地返回的lambda,特别是当这样的lambda被传递到另一个执行上下文时。换句话说,你不能在这样的lambda中使用return
。Q3:如果(3)没有产生性能改进,为什么(4)会产生性能改进?
答:
这是因为你在(3)中唯一的lambda被标记为
noinline
,这意味着你需要创建一个Function
对象来存放你的lambda的主体。对于(4),lambda仍然是内联的(性能提高),只是它不允许非本地返回。fjnneemd4#
关于第一及第二项问题
为什么(2)不能编译,而(4)可以?...
noinline
和crossinline
之间的区别这两种情况都有
inline
修饰符,指示内联函数test
和它的参数lambdaf
。inline修饰符既影响函数本身,也影响传递给它的lambda:所有这些都将被内联到调用点中。
因此编译器被指示放置代码(内联),而不是为
f
构造和调用函数对象。crossinline
修饰符仅用于内联的内容:它只是说传递的lambda(在f
参数中)不应该有非本地返回(“正常的”内联lambda可能有)。crossinline
可以被认为是这样的(编译器的指令):“执行内联,但有一个限制,即它会跨越调用者上下文,因此请确保lambda没有非本地返回。顺便说一句,
thread
看起来像是crossinline
的一个概念性说明示例,因为显然稍后在不同线程上从某些代码(传入f
)返回不可能影响从test
返回,test
继续在调用者线程上独立于其派生的代码执行(f
继续独立执行)。在案例#4中,有一个lambda(大括号)调用
f()
。在案例#2中,f
作为参数直接传递给thread
因此在#4中,调用
f()
可以被内联,编译器可以保证没有非本地返回。更详细地说,编译器会用它的定义替换f()
,然后代码被“ Package ”在封闭的lambda中,换句话说,{ //code for f() }
是另一种( Package 器)lambda,并且它本身进一步作为函数对象引用(到thread
)传递。在案例#2中,编译器错误只是简单地说它不能内联
f
,因为它作为引用被传递到一个“未知”(非内联)位置。crossinline
在这种情况下变得不合适和不相关,因为它只有在f
被内联时才能应用。总而言之,通过与Kotlin参考文献中的示例进行比较,情况2和4是不同的(参见“高阶函数和λ”):以下调用是等效的,其中大括号(lambda表达式)“替换” Package 器函数
toBeSynchronized
问题中的案例#2和#4不等同,因为在#2中没有调用
f
的“ Package 器