Kotlin:安全的 lambdas(无内存泄漏)? [英] Kotlin : safe lambdas (no memory leak)?

查看:17
本文介绍了Kotlin:安全的 lambdas(无内存泄漏)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

阅读之后这篇关于内存泄漏的文章,我想知道在 Kotlin Android 项目中使用 lambdas 是否安全.确实,lambda 语法让我更轻松地编程,但是内存泄漏呢?

After having read this article about Memory Leaks, I am wondering whether using lambdas in Kotlin Android project is safe. It's true that lambda syntax makes me program with more ease, but what about the Memory Leaks ?

作为有问题的一个例子,我从我的一个项目中提取了一段代码,在那里我构建了一个 AlertDialog.此代码位于我项目的 MainActivity 类中.

As an example of the problematic, I've taken a piece of code from one of my projects, where I build an AlertDialog. This code is inside the MainActivity class of my project.

fun deleteItemOnConfirmation(id: Long) : Unit {
        val item = explorerAdapter.getItemAt(id.toInt())
        val stringId = if (item.isDirectory) R.string.about_to_delete_folder else R.string.about_to_delete_file

        val dialog = AlertDialog.Builder(this).
                setMessage(String.format(getString(stringId), item.name)).setPositiveButton(
                R.string.ok, {dialog: DialogInterface, id: Int ->
                        val success = if (item.isDirectory) ExplorerFileManager.deleteFolderRecursively(item.name)
                        else ExplorerFileManager.deleteFile(item.name)
                        if (success) {
                            explorerAdapter.deleteItem(item)
                            explorerRecyclerView.invalidate()
                        }
                        else Toast.makeText(this@MainActivity, R.string.file_deletion_error, Toast.LENGTH_SHORT).show()
                    }).setNegativeButton(
                R.string.cancel, {dialog: DialogInterface, id: Int ->
                    dialog.cancel()
        })

        dialog.show()
}

我的问题很简单:为正负按钮设置的两个 lambda 会导致内存泄漏吗?(我的意思是,kotlin lambdas 是否只是转换为 Java 匿名函数?)

My question is very simple : can the two lambdas set for positive and negative buttons lead to Memory Leaks ? (I also mean, are kotlin lambdas simply converted to Java Anonymous functions ?)

也许我有我的答案 在这个 Jetbrains 主题中.

推荐答案

编辑(2017 年 2 月 19 日): 我收到了一个非常全面的 回复来自 Mike Hearn问题:

Edit (February 19, 2017): I received a very comprehensive reply from Mike Hearn regarding this issue:

就像在 Java 中一样,在 Kotlin 中发生的事情在不同情况下会有所不同.

Like in Java, what happens in Kotlin varies in different cases.

  • 如果 lambda 被传递给一个内联函数并且没有被标记为 noinline,那么整个事情就会消失并且没有额外的类或对象被创建.
  • 如果 lambda 没有捕获,那么它将作为单例类发出,其实例被一次又一次地重用(一个类+一个对象分配).
  • 如果 lambda 捕获,则每次使用 lambda 时都会创建一个新对象.

因此它与 Java 的行为相似,除了内联情况哪里更便宜.这种编码 lambda 的有效方法是 Kotlin 中的函数式编程更具吸引力的原因之一比在 Java 中.

Thus it is similar behaviour to Java except for the inlining case where it's even cheaper. This efficient approach to encoding lambdas is one reason why functional programming in Kotlin is more attractive than in Java.

<小时>

编辑(2017 年 2 月 17 日):我已在 Kotlin 讨论.也许 Kotlin 工程师会带来一些新的东西.


Edit (February 17, 2017): I've posted a question regarding this topic in the Kotlin discussions. Maybe Kotlin engineers will bring something new to the table.

kotlin lambdas 是否简单地转换为 Java 匿名函数?

are kotlin lambdas simply converted to Java Anonymous functions ?

我自己也在问这个问题(这里有一个简单的更正:这些被称为匿名类,而不是函数).Koltin 文档中没有明确的答案.他们只是声明

I was asking this question myself (one simple correction here: these are called Anonymous Classes, not functions). There is no clear answer in the Koltin documentation. They just state that

使用高阶函数会带来一定的运行时惩罚:每个函数是一个对象,它捕获一个闭包,即那些变量在函数体中访问.

Using higher-order functions imposes certain runtime penalties: each function is an object, and it captures a closure, i.e. those variables that are accessed in the body of the function.

在函数体中访问的变量的含义有点令人困惑.对封闭类的实例的引用也算在内吗?

It is a bit confusing what they mean by variables that are accessed in the body of the function. Is the reference to the instance of the enclosing class also counted?

我已经看到您在问题中引用的主题,但目前看来它已经过时了.我找到了更多最新信息 这里:

I've seen the topic you are referencing in your question but it seems it is outdated as for now. I've found more up-to-date information here:

Lambda 表达式或匿名函数保持隐式引用封闭类

Lambda expression or anonymous function keep an implicit reference of the enclosing class

因此,不幸的是,Kotlin 的 lambda 表达式似乎与 Java 的匿名内部类存在相同的问题.

So, unfortunately, it seems that Kotlin's lambdas have the same problems as Java's Anonymous Inner Classes.

来自 Java 规格:

类 O 的直接内部类 C 的实例 i 是关联的带有 O 的实例,称为 O 的直接封闭实例一世.对象的直接封闭实例(如果有)是在创建对象时确定

An instance i of a direct inner class C of a class O is associated with an instance of O, known as the immediately enclosing instance of i. The immediately enclosing instance of an object, if any, is determined when the object is created

这意味着匿名类将始终具有对封闭类实例的隐式引用.而且由于引用是隐式的,因此无法摆脱它.

What this means is that the anonymous class will always have an implicit reference to the instance of the enclosing class. And since the reference is implicit there is no way to get rid of it.

看一个简单的例子

public class YourActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        new Thread(new Runnable() {
                 // the inner class will keep the implicit reference to the outer activity
                @Override
                public void run() {
                 // long-running task
                }
        }).start();
   }
}

如您所见,在这种情况下,直到执行长时间运行的任务才会发生内存泄漏.一种解决方法是使用静态嵌套类.

As you can see, in this case there will be the memory leak until the long-running task is executed. One workaround for this is to use static nested class.

由于 Kotlin 的 非内联 lambdas 持有对封闭类实例的引用,因此它们在内存泄漏方面存在类似的问题.

Since Kotlin's non-inlined lambdas hold the reference to the instance of the enclosing class they have similar issues regarding memory leaks.

语法:

  • 声明SAM(单一抽象方法)接口

  • Declare SAM (single abstract method) interface

interface Runnable { void run(); }

  • 将此接口用作 lambda 的类型

  • Use this interface as a type for a lambda

    public void canTakeLambda(Runnable r) { ... }
    

  • 传递你的 lambda

  • Pass your lambda

    canTakeLambda(() -> System.out.println("Do work in lambda..."));
    

  • 内存泄漏问题:规格:

    对此的引用——包括通过隐式引用不合格的字段引用或方法调用——是,本质上,对最终局部变量的引用.Lambda 体包含此类引用捕获 this 的适当实例.在其他情况下,对象不保留对 this 的引用.

    References to this -- including implicit references through unqualified field references or method invocations -- are, essentially, references to a final local variable. Lambda bodies that contain such references capture the appropriate instance of this. In other cases, no reference to this is retained by the object.

    简单地说,如果您不使用封闭类中的任何字段/方法,则不会像匿名类那样隐式引用 this.

    Simply put, if you do not use any fields / methods from the enclosing class there is no implicit reference to this as in the case of anonymous classes.

    来自文档

    Lambda 表达式通过将它们转换为匿名来向后移植内部类.这包括使用 单例的优化无状态 lambda 表达式的实例以避免重复对象分配.

    Lambda expressions are backported by converting them to anonymous inner classes. This includes the optimization of using a singleton instance for stateless lambda expressions to avoid repeated object allocation.

    我想,这是不言自明的.

    I guess, it's self-explanatory.

    语法:

    • 声明类似于 Kotlin,在 Swift 中 lambdas 被称为闭包:

    • Declaration is similar to Kotlin, in Swift lambdas are called closures:

    func someFunctionThatTakesAClosure(closure: (String) -> Void) {}
    

  • 通过闭包

  • Pass the closure

    someFunctionThatTakesAClosure { print($0) }
    

    这里,$0 指的是闭包的第一个 String 参数.这对应于 Kotlin 中的 it.注意:与 Kotlin 不同,在 Swift 中我们还可以引用其他参数,例如 $1$2 等.

    Here, $0 refer to the closure’s first String argument. This corresponds to it in Kotlin. Note: Unlike Kotlin, in Swift we can refer also to the other arguments like $1, $2 etc.

    内存泄漏问题:

    在 Swift 中,就像在 Java 8 中一样,闭包仅在访问实例的属性时才捕获对 self(Java 和 Kotlin 中的 this)的强引用,例如self.someProperty,或者如果闭包调用实例上的方法,例如self.someMethod().

    In Swift, just like in Java 8, the closure captures a strong reference to self (this in Java and Kotlin) only if it accesses a property of the instance, such as self.someProperty, or if the closure calls a method on the instance, such as self.someMethod().

    此外,开发人员可以轻松指定他们只想捕获弱引用:

    Also developers can easily specify that they want to capture only the weak reference:

       someFunctionThatTakesAClosure { [weak self] in print($0) }
    

    我希望在 Kotlin 中也有可能 :)

    I wish it were possible in Kotlin too :)

    这篇关于Kotlin:安全的 lambdas(无内存泄漏)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆