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 ?
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()
}
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 ?)
Edit : Maybe I've got my answer in this Jetbrains Topic.
Edit (February 19, 2017): I received a very comprehensive reply from Mike Hearn regarding this issue:
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.
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 thatIt 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:
So, unfortunately, it seems that Kotlin's lambdas have the same problems as Java's Anonymous Inner Classes.
Why Anonymous Inner Classes are bad?
From the
Java
specs: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.
Look at the trivial example
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.
Since
Kotlin's
non-inlined lambdas hold the reference to the instance of the enclosing class they have similar issues regarding memory leaks.Bonus: Quick Comparison With Other Lambda Implementations
Java 8 Lambdas
Syntax:
Declare SAM (single abstract method) interface
Use this interface as a type for a lambda
Pass your lambda
Memory leak issues: As stated in specs:
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.Retrolambda
From the docs
I guess, it's self-explanatory.
Apple's Swift
Syntax:
Declaration is similar to Kotlin, in Swift lambdas are called closures:
Pass the closure
Here,
$0
refer to the closure’s firstString
argument. This corresponds toit
in Kotlin. Note: Unlike Kotlin, in Swift we can refer also to the other arguments like$1
,$2
etc.Memory leak issues:
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 asself.someProperty
, or if the closure calls a method on the instance, such asself.someMethod()
.Also developers can easily specify that they want to capture only the weak reference:
I wish it were possible in Kotlin too :)
Memory leaks happen when some object which should be removed because it isn't needed anymore can not be removed because something which has a longer lifetime has a reference to this object. The simplest example is storing the reference to the
Activity
in thestatic
variable (I'm talking from the Java perspective, but it's similar in Kotlin): after the user has clicked on 'Back' button theActivity
is not needed anymore, but it will be kept in memory nevertheless - because some static variable still points to this activity.Now, in your example you are not assigning your
Activity
to somestatic
variable, there're no Kotlin'sobject
s involved which could keep yourActivity
from being garbage-collected - all the objects involved in your code have roughly the same lifetime, which means there will be no memory leaks.P.S. I've refreshed my memories on the Kotlin's implementation of the lambdas: in the case of the negative button click handler you aren't referencing the outer scope, thus compiler will create single instance of the click listener which will be reused across all the clicks on this button. In the case of the positive button click listener, you're referencing the outer scope (
this@MainActivity
), so in this case Kotlin will be creating a new instance of the anonymous class each time you create a dialog (and this instance will have the reference to the outer class,MainActivity
), so the behavior is exactly the same as if you had written this code in Java.