Jat*_*ani 5 android memory-leaks memory-management textwatcher kotlin
我对 Kotlin 中的内存管理有基本的了解,即局部变量存储在堆栈中并由操作系统管理,对象在堆上创建并由 JVM 管理它们。此外,对象作为引用的副本传递,原始类型作为函数中的副本传递。
在下面的代码中,我创建了一个 TextEditOperation 函数,该函数以编程方式创建 EditText 并使用指令对象设置其属性,并向其添加 TextWatcher,最后将 edittext 视图添加到父相对布局。
public fun TextEditOperation (pInstructionSetIndex: Int, pInstructionIndex: Int): View?
{
val instruction: InstructionKernelAndroid
val context: Context?
val activity: Activity
val textedit: EditText
val parent_view: RelativeLayout
// fetch the instruction
instruction = InstructionSetMgr.uAndroidOSInstructionSetList[pInstructionSetIndex][pInstructionIndex]
// get activity's Context
context = ProcessStates.GetMainActivityContext ()
// cast activity's context to activity to use it's method
activity = context as Activity
// get parent view
parent_view = activity.findViewById (instruction.GetParentID ()) as RelativeLayout
// create view
textedit = EditText (context)
// set view properties
textedit.id = instruction.GetElementID ()
textedit.hint = instruction.GetHintText ()
textedit.layoutParams = RelativeLayout.LayoutParams (
instruction.GetWidth (),
instruction.GetHeight ()
)
textedit.x = instruction.GetX ()
textedit.y = instruction.GetY ()
textedit.setBackgroundColor (Color.parseColor (instruction.GetBackgroundColor ()))
textedit.setTextColor (Color.parseColor (instruction.GetTextColor ()))
// create and add a TextWatcher to the EditText
val text_watcher: TextWatcher
text_watcher = object : TextWatcher {
override fun beforeTextChanged (s: CharSequence, start: Int, count: Int, after: Int)
{
}
override fun onTextChanged (s: CharSequence, start: Int, before: Int, count: Int)
{
TextChanged (pInstructionSetIndex + 1, textedit.length (), textedit.id)
}
override fun afterTextChanged (s: Editable)
{
}
}
// add the textwatcher
textedit.addTextChangedListener (text_watcher)
// add view to parent view
parent_view.addView (textedit);
return textedit;
}
Run Code Online (Sandbox Code Playgroud)
现在,我的理解是,如果 text_watcher 对象不存在,那么对于 line
textedit = EditText(上下文)
EditText(context) 对象将在堆上创建(由 JVM 管理),引用该对象的变量 textedit(由操作系统管理)将在堆栈上创建。因此,一旦函数作用域结束,textedit 变量就会被操作系统从堆栈内存中清除。
但是,现在我在 text_watcher 的 OnTextChanged 函数中使用 textedit 变量:
override fun onTextChanged (s: CharSequence, start: Int, before: Int, count: Int)
{
TextChanged (pInstructionSetIndex + 1, textedit.length (), textedit.id)
}
Run Code Online (Sandbox Code Playgroud)
在 TextEditOperation 的函数作用域结束后, onTextChanged 函数仍将被调用。我如何能够使用现在应该已被操作系统删除的 textedit 变量访问 EditText 的长度和 id?
是否是因为 kotlin 中的闭包,比我能够访问外部作用域中存在的变量,因此它无法被操作系统释放,从而导致内存泄漏,如果是,如何修复它,或者还有其他原因吗后面呢?
免责声明:当针对 JVM 时,本答案重点关注 Kotlin。一般概念应该适用于其他平台,但具体细节可能有所不同。
\n当闭包使用封闭范围中的变量时,该闭包将在创建闭包时“捕获”该变量的值。
\n在 Kotlin 中,这是通过向为闭包对象生成的类隐式添加类属性来实现的。该属性在闭包实例化期间使用捕获的变量的值进行初始化。在闭包的范围内,任何时候看起来您正在使用捕获的变量,您实际上正在使用该属性。所有这些都是由编译器在幕后为您完成的。
\n因此,textedit在您的TextWatcher实现中使用是完全可以的。
该变量的值是在TextWatcher创建时捕获的,而不是在实现中首次“使用”该变量时捕获的。
textedit对实现范围内的所有引用TextWatcher实际上都在引用所述实现的隐藏的、隐式定义的属性。这意味着在局部变量从堆栈中弹出后,不会尝试访问它。
请注意,当闭包引用其封闭类的类属性时,闭包实际上捕获封闭类的实例(即this),而不是直接捕获属性的值。此外,与 Java 不同,Kotlin 能够捕获可变局部变量(即var),这是通过使用隐藏的“包装类”来包装实际值,然后捕获该包装对象来实现的。
下面是一些代码,演示了编译器正在执行的操作的概念。这不一定是变量捕获的具体实现方式,但它应该有助于理解这个想法。
\n如果你有这样的事情:
\ninterface Printer {\n fun print()\n}\n\nfun createPrinter(): Printer {\n val message = "Hello, World" // local variable\n return object : Printer {\n override fun print() = println(message)\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n编译后,就好像你写了以下内容:
\ninterface Printer {\n fun print()\n}\n\nfun createPrinter() : Printer {\n val message = "Hello, World" // local variable\n class AnonymousPrinterImpl(\n private val message: String // class property\n ): Printer {\n // Here, \'message\' is referring to the class property,\n // not the local variable.\n override fun print() = println(message)\n }\n return AnonymousPrinterImpl(message)\n}\nRun Code Online (Sandbox Code Playgroud)\n如果您想查看编译器实际生成的内容,则可以通过该javap工具检查 Java 字节码。请注意,匿名对象的实现与函数接口或 lambda 表达式不同,但最终结果是相同的:类属性被隐式添加到闭包的类中。
在 Kotlin 中,堆内存由垃圾收集器管理。从较高层次来看,垃圾收集器的工作方式是从指定的根对象开始定期遍历当前对象图。任何无法从这些根对象到达的对象都可以进行垃圾回收。在具有垃圾收集器的语言中,当程序不再需要某个对象后仍保持强引用时,就会发生“内存泄漏”。强引用可以防止垃圾收集器收集不需要的对象。
\n您似乎担心函数返回后TextWatcher错误地将对象保留在内存中。TextEdit但是您正在TextEdit从函数返回对象并将其添加到某个“父视图”中。这两件事都强烈表明TextEdit在函数返回后您的程序仍然需要该对象。
此外,函数返回后对 的唯一强引用TextWatcher是TextEdit. 在这种情况下,并不是将TextWatcher保留TextEdit在内存中,而是相反。当TextEdit变得有资格进行垃圾回收时,TextWatcher. 两者之间的循环引用不会改变这一点。\xc2\xa0
简而言之,您的代码中没有内存泄漏;或者至少不是由TextWatcher您向我们展示的内容引起的。
| 归档时间: |
|
| 查看次数: |
172 次 |
| 最近记录: |