Kotlin的crossinline和noinline有什么不同?
-
此代码编译时会有一个警告 (对性能影响不明显 ):
inline fun test(noinline f: () -> Unit) { thread(block = f) }
-
此代码不能编译 ( 非法使用内联参数 ):
inline fun test(crossinline f: () -> Unit) { thread(block = f) }
-
此代码编译时会有一个警告 (对性能影响不明显 ):
inline fun test(noinline f: () -> Unit) { thread { f() } }
-
此代码编译没有警告或错误 :
inline fun test(crossinline f: () -> Unit) { thread { f() } }
这是我的问题:
- (2)如何不编译,但(4)呢?
-
noinline
和crossinline
什么crossinline
? - 如果(3)不产生一个没有性能改进,为什么会(4)呢?
来自内联函数的参考 :
请注意,某些内联函数可能会将传递给它们的lambda作为参数,而不是直接从函数体中调用,而是从另一个执行上下文(如本地对象或嵌套函数)中调用。 在这种情况下,lambda中也不允许非本地控制流量。 为了表明,lambda参数需要用crossinline修饰符标记
因此,示例2不会编译,因为crossinline
仅执行本地控制流,而表达式block = f
违反了这一点。 例1编译,因为noinline
不需要这样的行为(显然,因为它是一个普通的函数参数)。
示例1和3不会产生任何性能改进,因为唯一的lambda参数被标记为noinline
,使该函数的inline
修饰符无用且冗余 – 编译器想要内联某些东西,但是所有可能被标记为不是内联。
考虑两个功能, A和B.
一个
inline fun test(noinline f: () -> Unit) { thread { f() } }
乙
fun test(f: () -> Unit) { thread { f() } }
函数A的行为就像函数B一样,参数f
不会内联( B函数不内联test
体,而在A函数中,body: thread { f() }
仍然被内联)。
现在,在例4中这是不正确的,因为crossinline f: () -> Unit
参数可以内联,所以它不能违反上述非局部控制流规则(就像将新值赋给全局变量一样)。 如果可以内联,编译器会假设性能得到改进,并不像示例3那样发出警告。
Q1:怎么(2)不编译,但是(4)呢?
从他们的文档:
Inlinable lambda只能在内联函数中调用或作为inlinable参数传递…
回答:
thread(...)
方法thread(...)
不是inline
方法,所以你不能将f
作为参数传递。
Q2:noinline和crossinline的区别是什么?
回答:
noinline
将阻止lambda的内联。 当你有多个lambda参数,并且你只想把一些lambda表达式传递给一个内联函数的时候,这会变得很有用。
crossinline
用于标记不允许非本地返回的lambdas,特别是当这样的lambda传递给另一个执行上下文时。 换句话说,你将无法在这样的lambda表达式中使用return
。 用你的例子:
inline fun test(crossinline f: () -> Unit) { thread { f() } } //another method in the class fun foo() { test{ //Error! return is not allowed here. return } }
问题3:如果(3)没有产生一个没有性能改进,为什么会(4)呢?
回答:
那是因为你在(3)中唯一的lambda已经被标记为noinline
,这意味着你将有创建Function
对象来容纳你的lamda的开销。 对于(4)lambda仍然内联(性能改进)只是它不会允许非本地回报。
第一个和第二个问题
(2)如何不编译,但是(4)呢?
noinline
和crossinline
之间的crossinline
2. inline fun test(crossinline f: () -> Unit) { thread(block = f) } 4. inline fun test(crossinline f: () -> Unit) { thread { f() } }
这两种情况都有inline
修饰符指示内联函数test
及其参数lambda f
。 从kotlin参考:
内联修饰符影响函数本身和传递给它的lambda表达式:所有这些将被内联到调用站点中。
所以编译器被指示放置代码(内联),而不是为f
构造和调用一个函数对象。 crossinline
修饰符仅用于内联事物:它只是说传递的lambda(在f
参数中)不应该有非本地返回(这是“正常”内联lambda可能有)。 crossinline
可以被认为是这样的(对编译器的指令):“做内联,但有一个限制,它跨越了调用者上下文,所以确保lambda没有非本地返回。
在一个侧面说明, thread
似乎是一个概念上说明crossinline
thread
例子,因为显然从一个不同的线程返回一些代码(在f
传递)不能影响从test
的返回,它继续在调用者线程上执行,催生( f
继续独立执行)
在情况#4,有一个lambda(花括号)调用f()
。 在情况#2中, f
直接作为参数传递给thread
所以在#4中,调用f()
可以内联,编译器可以保证没有非本地返回。 详细来说,编译器会用它的定义代替f()
,然后代码被封装在封装的lambda中,换句话说, { //code for f() }
是另外一种(包装器)lambda,它本身进一步作为函数对象引用传递给( thread
)。
在#2的情况下,编译器错误只是说它不能内联f
因为它被作为参考传递到“未知”(非内联)的地方。 crossinline
变得crossinline
,在这种情况下是不相关的,因为只有f
被内联才能适用。
总结起来,情况2和情况4与kotlin参考的例子不同(参见“高阶函数和Lambdas”):下面的调用是等价的,其中大括号(lambda表达式)“替换”包装器函数被toBeSynchronized
//want to pass `sharedResource.operation()` to lock body fun <T> lock(lock: Lock, body: () -> T): T {...} //pass a function fun toBeSynchronized() = sharedResource.operation() val result = lock(lock, ::toBeSynchronized) //or pass a lambda expression val result = lock(lock, { sharedResource.operation() })
在问题中的情况#2和#4是不相同的,因为在#2中没有调用f
“包装器”