在正常的程序操作中,全部释放一个对象的所有引用以后才会破坏该对象。这就是对象的存活期的含义。在这个主题中,为了说明这个问题,我们将以一种特殊的方式来使用 Thing 对象。
注意 创建一个 ActiveX DLL 示例需要分为几步,这个帮助主题只是其中一步。要访问该帮助主题,选择帮助主题“创建 ActiveX DLL”即可。
要在 Thing 类中添加 StuckOnMyself 属性,请按照以下步骤执行:
'StuckOnMyself
属性的私有数据。Private mthStuckOnMyself As Thing
Private mblnStuckOnMyself As Boolean
修改属性过程如下所述:
Public Property Get StuckOnMyself() As Boolean
StuckOnMyself = mblnStuckOnMyself
End Property
Public Property Let StuckOnMyself(ByVal NewValue _
As Boolean)
mblnStuckOnMyself = NewValue
If mblnStuckOnMyself Then
Set mthStuckOnMyself = Me
Else
Set mthStuckOnMyself = Nothing
End If
End Property
这段代码说明了属性过程的作用。当一个用户需要得到 StuckOnMyself 属性的值时,可以调用 Property Get 过程,这时只是简单地返回模块级的布尔变量 mblnStuckOnMyself
的值。
当用户设置 StuckOnMyself 属性的值时,则调用 Property Let 过程。在为模块级的布尔变量 mblnStuckOnMyself
赋新值之后,Property Let 或者将模块级变量 mthStuckOnMyself
赋值为 Thing 对象 (Me) 的引用,或者将其设为 Nothing。
详细信息 对于部件提供的对象,要实现它的属性,最好的办法就是属性过程。请参阅“部件设计的一般准则”的“部件中属性的实现”。
如果两个对象相互引用,或者一个对象引用了它自己,那么就会产生循环引用的问题,StuckOnMyself 属性提供了这样一个例子。关于循环引用的详细信息,请参阅“部件设计的一般准则”中的“循环引用的处理”。
下一步向 TestThing 中添加新的代码,有选择地设置 StuckOnMyself 属性来说明什么是循环引用。同时,它还介绍了创建对象的另一种办法。
要通过 TestThing 测试 StuckOnMyself 属性,请按照以下步骤执行:
'
按钮"Temporary Thing"
。Private Sub Command5_Click()
Dim thTemp As New Thing
thTemp.Name = InputBox( _
"Enter a name for the temporary Thing", _
"Temporary Thing")
'
如果标题为“Stuck on itself
”的复选框被选中,'
则创建一个循环引用。If Check1.Value = vbChecked Then
thTemp.StuckOnMyself = True
End If
End Sub
复选框不需要任何代码。如果 Check1 被选中,则 Command5_Click 事件过程会设置新的 Thing 对象的 StuckOnMyself 属性。
请注意,在这里并没有用 New 操作符来明确地创建新的 Thing,“Temporary Thing”按钮使用了一个声明为 As New 的变量,这样就可以隐含地创建对象了。下面的步骤将对此进行说明,同时,它还说明变量的作用域是如何影响对象的存活期的,以及循环引用对对象存活期的影响。
要用 TestThing 说明循环引用,请按照以下步骤执行:
thTemp
是过程级的变量,因此它的存活期(以及相应对象的存活期)就被限制在过程的执行时间内。
首先看到的是一个“InputBox”,因为在为新的 Thing 对象的 Name 属性赋值之前,Visual Basic 必须先计算等号右边的代码。
在向输入框中输入名称之前,请先看一看“立即”窗口中的内容。由于新的 Thing 对象尚未创建,因此没有 Thing 发来的 Initialize 消息。由于变量 thTemp
被声明为 As New,因此在调用 Thing 对象的属性或方法时将创建 Thing 对象(不会在这之前)。
这时在“立即”窗口中将会看到两条消息,一条 Initialize 消息和一条 Terminate 消息。在把从 InputBox 获得的名称赋值给 thTemp.Name
时产生了 Initialize 事件。Visual Basic 发现 thTemp
包含的是 Nothing,于是创建 Thing 对象,并将对该对象的引用放入 thTemp
中。
尽管 DebugID 属性的值已经被设置(在 Initialize 事件中最早完成的),但 Name 属性仍然是空的。这证实 Initialize 事件是最早发生的,先于所有其它代码的,也先于其它所有属性的设置。
只有在完成所有这些之后,Visual Basic 才能把从 InputBox 函数获得的值赋给 Thing 的 Name 属性。一行代码可以引发很多的活动。
事情还没有结束。在创建了 Thing 之后,Command5_Click 事件过程立即就结束了。变量 thTemp
离开了它的作用域,因此如同被设置成 Nothing 一样。现在没有对临时的 Thing 的引用了,因此它被破坏了。它的 Terminate 事件显示了它的属性,包括赋给它的名称。
连续按几次 F8 键,一直运行到设置 Thing 的名称的那一行:
在进入下一步之前,请猜一猜接下来执行的会是哪一行。
由于调试 ThingDemo 部件的环境与测试程序是同一个,Visual Basic 可以从测试程序中直接进入到部件中的代码。
继续按 F8 键,运行 Initialize 事件、DebugID 属性以及 Terminate 事件中的代码。当运行到 Class_Terminate() 的最后一行时,按 F5 键返回到运行模式。
要了解部件中发生事件的顺序,进程内调试是一个强有力的工具。
这时,在“立即”窗口中可以看到一条来自 Thing 的 Initialize 消息,但是没有 Terminate 消息。在变量 thTemp
离开作用域时,对象不会被破坏。因为仍旧存在对该对象的引用(在 StuckOnMyself 属性的 mthStuckOnMyself
变量中)。
那么,在什么时候破坏原来的临时 Thing 呢?如果能够把 Renegade Thing 的 StuckOnMyself 属性设置成 False,那么就不会有更多的引用了,但是 TestThing 不能这样做,因为程序已经没有用来调用 StuckOnMyself 的引用了。
这样对象就成为孤立的,在 DLL 卸载之前它将一直占用内存。在后面的步骤中可以看到,Renegade Thing 的 StuckOnMyself 属性中的循环引用将导致整个 DLL 保留在内存中。
在观察第一个 Renegade 对象时,在“立即”窗口中将看不到这些对象的 Terminate 消息,因为循环引用将阻止破坏它们。
在 Visual Basic 关闭 TestThing 时,它还会关闭 ThingDemo。按照一定的顺序,它将清除 ThingDemo 的所有对象变量,其中包括 Renegade Thing 和它的后继者对自己的引用。
可以看到,在进程内部件中避免多余的对象引用是极为重要的。客户应用程序在使用部件时可以创建和释放几百个对象。如果对象都象 Renegade Thing 那样保留在内存中,性能将不可避免地降低。
详细信息 关于循环引用,请参阅“部件设计的一般准则”中的“循环引用的处理”,以及附录 B,“ActiveX 部件标准及指南”。
创建 ActiveX DLL 示例需要好几步,该帮助主题只是其中一步。
目的 | 请参阅 |
到下一步 | 在 ThingDemo 工程中添加窗体 |
从头开始 | 创建一个 ActiveX DLL |