如何优化使用UserControl避免界面卡顿问题
扫描二维码随身看资讯
使用手机 二维码应用 扫描右侧二维码,您可以
1. 在手机上细细品读~
2. 分享给您的微信好友或朋友圈~
写在前面:
在软件开发过程中,面对复杂的界面需求时,使用UserControl将不同模块的界面设计单独封装是一个不错的选择。这种做法能够应对客户频繁的需求更改,动态加载预先设计好的特定模块的UserControl,避免用代码对界面进行复杂的控制。虽然在设计之初费力,但后续维护起来相对方便。
背景介绍:
最近在开发新工具时,为了灵活的进行数据初始化,我设计了不同的布局并单独封装为UserControl,放置在PanelControl中作为数据展示。为了实现数据的动态加载,我给每个UserControl都订阅了主程序的通知事件。通过委托回调的方式,主程序中调用委托,子控件中自动执行具体的初始化方法。
//定义一个带有一个参数的无返回值的委托
delegate void DelegateUpdateUserControl(DeviceConfig item);
//声明委托对象
private DelegateUpdateUserControl UpdateUserControl;
大事件发生:
在测试过程中,数据的动态加载以及UserControl的切换展示没有问题。但随着切换次数的增加,界面变得越来越卡顿。
我开始怀疑UserControl对象仍在被主程序引用,系统无法进行GC.Collect(),对象没有被回收。为了验证这一点,我在UserControl中的订阅方法里增加一行Console.WriteLine方法进行测试。
测试:
理想情况下,每切换一次UserControl,会调用一次委托,让UserControl绑定的委托方法输出一条信息。测试却发现,第一次会输出1条、第二次输出2条、第三次输出3条...除了第一次调用委托方法,后面每次调用都有不止一个对象在响应且数量依次增加,看来确实是每次的UserControl都没有被成功回收。
结合.NET的垃圾回收机制,断定UserControl对象还在被主程序引用,导致没有被成功释放,可能是没有解除委托与方法的绑定造成的。
优化解决方案:
我对代码进行了优化,修改了UserControl的切换方式,并主动解除委托绑定,及时让GC回收资源。
//在切换控件时,先清空原先的旧UserControl
//清空PanelControl所有控件,通常情况下只存在一个控件
PanelControlContainer.Controls.Clear();
改为:
if (PanelControlContainer.Controls.Count > 0)
{
//获取到UserControl(PanelControlContainer)对象
UControlProperty control =(UControlProperty)PanelControlContainer.Controls[0];
//取消事件挂载,解除委托绑定
UpdateUserControl -= control.UpdateControls;
//从PanelControl中移除旧UserControl
PanelControlContainer.Controls.Remove(control);
//主动释放资源
control.Dispose();
}
以上步骤主要是取消事件挂载,解除主程序与UserControl对象之间的引用关系,及时让GC回收资源。
结果:
经最后测试,调用委托后,只会有当前最新的UserControl对象响应,界面切换时卡顿的现象消失。
扩展——.NET垃圾回收机制:
遇到这种情况,我们必须对.NET的垃圾回收机制有一定了解。在C#中,当我们创建一个对象时,它的引用会被存储在栈(Stack)中,而对象的实际数据会被存储在堆(Heap)中。这是.NET运行时自动进行的内存管理,称为垃圾回收(Garbage Collection, GC)。
- 每个对象都有两个开销字段:类型对象指针和同步块索引,在64位应用程序中各占8个字节。
- 所有引用类型的变量都称为根对象,这些根对象是垃圾回收的起点,包括局部变量、全局变量、静态字段等。
- CLR开始回收时会暂停所有线程,防止线程在CLR检查期间访问对象并更改其状态。
- 然后CLR进入GC的标记阶段,CLR遍历堆中的所有对象,将同步索引块中的一位设为0。
- 然后CLR检查所有活动根,任何根引用了堆上的对象,CLR都会标记那个对象,将同步索引块中的位设为1。
- 一个对象被标记后,CLR会检查那个对象的根,标记他们引用的对象,如果对象已经被标记,就不重新检查对象的字段,避免引起死循环。
- 检查结束后,已标记的对象至少有一个根在引用,我们说这种对象是可达的,不能被垃圾回收。
- GC删除未被标记的对象,把被标记的对象挪到一块连续的空间,进行压缩,避免空间碎片化的问题。
- 作为压缩的一个步骤,CLR还要从每个根减去所引用对象在内存中的偏移字节数,保证每个根引用的是之前的对象。
- 最后恢复执行之前被暂停的线程。
总结:
给对象事件绑定委托后,释放对象前,要主动解除委托绑定,避免出现资源无法释放的问题。
- 1
新麻将连连看 消消乐
- 2
托卡3D版全部版中文版下载 v2.2.2 安卓版
- 3
天天酷跑3d单机游戏
- 4
茶香世家
- 5
植物大战僵尸杂交版 最新免费下载
- 6
人类游乐场 安卓免费版
- 7
猎鱼达人VIVO版本下载 v3.7.0.0 安卓版
- 8
植物大战僵尸幼儿园版 安卓官方版
- 9
希望之村2来生
- 10
米加小镇 免费无广告最新版
- 1
加查之花 正版
- 2
爪女孩 最新版
- 3
捕鱼大世界 无限金币版
- 4
企鹅岛 官方正版中文版
- 5
内蒙打大a真人版
- 6
跳跃之王手游
- 7
情商天花板 2024最新版
- 8
烦人的村民 手机版
- 9
球球英雄 手游
- 10
大富翁go 官网版