Kotlin:安全的 lambda(无内存泄漏)?

2024-01-10

读完后这篇关于内存泄漏的文章 https://medium.com/freenet-engineering/memory-leaks-in-android-identify-treat-and-avoid-d0b1233acc8#.yet4778q1,我想知道在 Kotlin Android 项目中使用 lambda 是否安全。确实,lambda 语法使我的编程更加轻松,但是内存泄漏呢?

作为问题的一个例子,我从我的一个项目中获取了一段代码,我在其中构建了一个 AlertDialog。这段代码位于我的项目的 MainActivity 类中。

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 lambda 是否只是简单地转换为 Java 匿名函数?)

编辑:也许我已经找到答案了在这个 Jetbrains 主题中 https://discuss.kotlinlang.org/t/function-literals-and-reference-to-enclosing-class/416.


编辑(2017 年 2 月 19 日):我收到了非常全面的reply https://discuss.kotlinlang.org/t/lambdas-and-implicit-references-to-the-instance-of-the-enclosing-class/2288/2?u=storix迈克·赫恩 (Mike Hearn) 关于此问题的评论:

与 Java 一样,Kotlin 中发生的情况在不同情况下会有所不同。

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

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


编辑(2017 年 2 月 17 日):我已在 中发布了有关此主题的问题Kotlin 讨论 https://discuss.kotlinlang.org/t/lambdas-and-implicit-references-to-the-instance-of-the-enclosing-class/2288。也许 Kotlin 工程师会带来一些新东西。


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

我自己问这个问题(这里一个简单的更正:这些被称为匿名类,而不是函数)。文中并没有明确的答案Koltin文档。他们只是state https://kotlinlang.org/docs/reference/inline-functions.html that

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

他们的意思有点令人困惑在函数体中访问的变量。对封闭类实例的引用也被计算在内吗?

我已经看到您在问题中引用的主题,但目前看来它已经过时了。我找到了更多最新信息here https://discuss.kotlinlang.org/t/lambda-expression-or-anonymous-function-keep-an-implicit-reference-of-the-enclosing-class/1429:

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

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

为什么匿名内部类不好?

来自Java specs https://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html:

关联类 O 的直接内部类 C 的实例 i 与 O 的实例,称为直接封闭实例 我。对象的直接封闭实例(如果有)是 对象创建时确定

这意味着匿名类总是有一个implicit对封闭类的实例的引用。由于引用是隐式的,因此无法摆脱它。

看看这个简单的例子

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();
   }
}

正如您所看到的,在这种情况下,在执行长时间运行的任务之前,将会出现内存泄漏。解决此问题的一种方法是使用静态嵌套类。

Since Kotlin's 非内联lambda 保留对封闭类实例的引用,它们在内存泄漏方面也有类似的问题。

奖励:与其他 Lambda 实现的快速比较

Java 8 Lambda 表达式

Syntax:

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

    interface Runnable { void run(); }
    
  • 使用此接口作为 lambda 的类型

    public void canTakeLambda(Runnable r) { ... }
    
  • 通过你的 lambda

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

内存泄漏问题:如中所述specs http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html:

对此的引用——包括通过 不合格的字段引用或方法调用——是, 本质上,是对最终局部变量的引用。拉姆达机构 包含此类引用捕获此的适当实例。在 其他情况,不保留对此的引用通过对象。

简而言之,如果您不使用封闭类中的任何字段/方法,则不会隐式引用this就像匿名类的情况一样。

逆转录酶

来自docs https://github.com/orfjackal/retrolambda

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

我想,这是不言自明的。

苹果的斯威夫特

Syntax:

  • 声明与 Kotlin 类似,在 Swift 中 lambda 称为闭包:

    func someFunctionThatTakesAClosure(closure: (String) -> Void) {}
    
  • 通过关闭

    someFunctionThatTakesAClosure { print($0) }
    

    Here, $0参考闭包的第一个String争论。这对应于it在科特林中。注意:与 Kotlin 不同,在 Swift 中我们还可以引用其他参数,例如$1, $2 etc.

内存泄漏问题:

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

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

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

我希望 Kotlin 也能做到这一点:)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Kotlin:安全的 lambda(无内存泄漏)? 的相关文章

随机推荐

  • 组件中未定义渲染函数或模板:匿名

    我遇到这样的问题 重新加载页面后出现错误 组件中未定义渲染函数或模板 匿名 我认为该错误是由于 socket io 造成的 这个错误不是发生在本地机器上 而是发生在生产上 这是怎么回事 开发者 https dev jujuway com u
  • 如何在Python中编写字符串文字而不必转义它们?

    有没有一种方法可以在Python中声明一个字符串变量 这样它里面的所有内容都会自动转义 或者有它的文字字符值 I m not询问如何用斜杠转义引号 这是显而易见的 我要求的是一种通用的方法 用于将所有内容都放入字符串文字中 这样我就不必手动
  • 在查询中使用 TOP 1 时出现问题

    我编写了以下查询来获取日期 删除它的时间部分并添加我想要的时间 如果我在没有 TOP 子句的情况下运行此查询 则效果很好 但是当我添加它时 它返回以下异常 从字符串转换日期和 或时间时转换失败 这是查询 SELECT TOP 1 CONVE
  • C# List .ConvertAll 效率和开销

    我最近了解了 List 的 ConvertAll 扩展 我今天在工作中在代码中使用了几次它 将我的大量对象列表转换为其他对象的列表 看起来效果真的很好 但是 我不确定与仅迭代列表和转换对象相比 这有多高效或多快 ConvertAll 是否使
  • Android 应用内计费、非消耗品

    我正在实施应用内计费 用户将能够购买优质内容的访问权限 这是典型的非消耗品 假设优质内容是问题应用程序中的额外问题或类别 我用过this http www techotopia com index php Integrating Googl
  • 如何设置UILabel发光效果的颜色?

    我们通常可以设置fontColorUILabel 的实现方式 label textColor self someTextColor 和阴影 发光 label layer shadowColor self someGlowColor labe
  • 如何在shell脚本中调用函数?

    我有一个有条件调用函数的 shell 脚本 例如 if choice true then process install elif choice false then process exit fi process install comm
  • 通过句子或段落训练和评估 spaCy 模型

    观察 段落 I love apple I eat one banana a day句子 I love apple I eat one banana a day这一段有两句话 I love apple and I eat one banana
  • 无法添加实体类型“X”的种子实体,因为没有为所需属性“..ID”提供值

    我在玩机智EF Core 2 1 预览 2 我在使用 HasData Seed 方法时遇到问题OnModelCreating ModelBuilder modelBuilder 我的模型是简单的 POCO 类 没有注释 public cla
  • 无法使用“Regex::replace()”来替换编号的捕获组

    我正在将复数器移植到 Rust 但在使用正则表达式时遇到了一些困难 我无法获取Regex replace 正如我所期望的那样 替换编号捕获组的方法 例如 以下显示空字符串 let re Regex new m l ouse unwrap p
  • 从文件开头删除字节数

    我想复制一个没有前 256 个字节的文件 有没有一个好的方法可以用 python 来做到这一点 我猜想简单的方法是用计数器读取字节 然后仅在达到 256 时才开始复制 我希望有更优雅的方式 Thanks with open input rb
  • 如何在复杂数据的情况下分离行

    说实话这个问题本来就不是我的 这个问题 https stackoverflow com q 67871768 2884859促使我把它放在一个简化的案例中 因此 我必须根据分隔符将输入到单元格 列中 的数据分隔成单独的行 即 在目前的情况下
  • 我从脚本创建了文本区域扩展器,但之后它没有扩展

    不知道标题是否正确 我使用一个脚本创建了一张包含 5 个文本区域且 class expand 的表 当我写入时 该文本区域会扩展 但随后不起作用 写完之后有调用jquery插件的方法吗 在我尝试不创建文本区域之前 我在 html 文件中写入
  • Bash 脚本将文本文件与文件名中的特定子字符串连接起来

    在某个目录中 我有许多包含一堆文本文件的目录 我正在尝试编写一个脚本 仅将每个目录中文件名中包含字符串 R1 的文件连接到该特定目录中的一个文件中 以及将那些文件名中包含 R2 的文件连接到另一个 这是我写的 但它不起作用 bin bash
  • 如何使用设备ID获取USB硬件ID?

    如何使用设备ID获取USB设备的硬件ID 我使用的是vc 6 0 操作系统是xp 使用wmi可以吗 终于解决了我的问题 谢谢你的回复 我在这里发布代码 它可能对某人有用 通过这段代码 我们可以获取与我们系统连接的设备的所有硬件ID HDEV
  • 为什么在 WPF 中的菜单上操作时 TextBox 不会失去焦点?

    如果我编辑与 ViewModel 绑定的 TextBox 上的文本 然后单击主应用程序菜单 以保存数据 ViewModel 不会使用新数据进行更新 因为不知何故 TextBox 不会失去焦点 然后数据未更新 为什么文本框不会失去焦点 我应该
  • Python循环引用

    尝试在同一个文件中拥有两个相互引用的类 让它发挥作用的最佳方法是什么 class Foo object other Bar class Bar object other Foo if name main print all ok 问题似乎在
  • Android 空指针异常 不确定如何修复它

    我的代码中的一行不断收到空指针异常错误 但我不知道如何修复它 它基本上只是另一种方法的副本 所以我不知道为什么它会给我错误 任何帮助 将不胜感激 import java util Calendar import android app Ac
  • Java 上有关无效 XML 字符的错误

    在 Java 上解析 xml 文件时出现错误 An invalid XML character Unicode 0x0 was found in the element content of the document xml 来自 web
  • Kotlin:安全的 lambda(无内存泄漏)?

    读完后这篇关于内存泄漏的文章 https medium com freenet engineering memory leaks in android identify treat and avoid d0b1233acc8 yet4778