关于循环引用:COM的interface还有高低之说呢?

slowgrace 2009-07-05 04:02:54
加精
MSDN里有一节讲循环引用,讲得挺吓人的。

所谓循环引用指的是:一个对象A的属性对象C,属性对象C通过C的parent属性又引用到对象A。这样A和C就构成循环引用。

在VB里循环引用会导致对象久久不能被释放,即使你set nothing之后。而如果你终止了整个应用之后,虽然对象可以被释放,但仍然会有很多孤儿对象留在内存里直到你关机。想起前一阵写树类的时候我到处都在用这种循环引用啊,汗。

里面讲到一个Excel的Button对象的例子:Microsoft Excel is written using C++ and low level COM interfaces, and it maintains the Parent properties of its objects without creating circular references.

这个的话,COM的interface还有高低之说呢?那像treeview这样的VB自带的ActiveX控件通常是用什么做的呢?



注:MSDN里的目录位置:Visual Tools and Languages\Visual Studio 6.0 Documentation\Visual Basic Documentation\Using Visual Basic\Component Tools Guide\Creating ActiveX Components\Creating ActiveX Components\Organizing Objects: The Object Model\Dealing with Circular References

...全文
519 59 打赏 收藏 转发到动态 举报
写回复
用AI写文章
59 条回复
切换为时间正序
请发表友善的回复…
发表回复
贝隆 2009-07-14
  • 打赏
  • 举报
回复
除了顶,还是顶
pusiyu 2009-07-07
  • 打赏
  • 举报
回复
呃,感觉很复杂,好难,学习中,ING。。。。。。
bj0629 2009-07-07
  • 打赏
  • 举报
回复
都很敬业、佩服!
DreamFreeLancer 2009-07-06
  • 打赏
  • 举报
回复
循环引用是基于“引用计数”的对象生存期管理可能出现的一个“副作用”,并非如楼上有些人所说是智能指针引入的问题。也并非COM所独有,我认为“引用计数”绝对应归入“设计模式”,COM只是恰巧使用而已。“引用计数”机制的一个一般性原则:持有引用必加计数,但这在某些情况下确实会引起棘手的“循环引用计数”问题,要避免这种情况,主要还要靠程序员引入其它辅助的对象生存期管理策略。比如:一个对象叫“毛”,一个对象叫“皮”。为访问方便,通常会在“毛”中持有“皮”的引用,并将“皮”的引用计数加一;在“皮”中持有“毛”的引用,但这时不必对“毛”的引用计数加一,否则,将出现“循环引用”,这里可以不加一的原因是,你知道“毛”的生存期永远从属于“皮”--“皮之不存,毛将焉附”。“引用计数”是好的,但它不是万能的,在特定的场景中,领域知识会帮助你避免循环引用问题。
有关引用计数更多的知识可参阅本人的博克:http://blog.csdn.net/DreamFreeLancer/archive/2009/05/20/4202951.aspx
Tiger_Zhao 2009-07-06
  • 打赏
  • 举报
回复
48楼的引用应该是
[Quote=引用楼主 slowgrace 的回复:]
using C++ and low level COM interfaces[/Quote]
Tiger_Zhao 2009-07-06
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 slowgrace 的回复:]using C++ and low level COM interfaces[/Quote]
只要实现了 IUnknown 就算一个 "COM 对象",这大概就是所谓的 "low level"。
而 "VB 对象" 必须实现 IDispatch。
sugargg 2009-07-06
  • 打赏
  • 举报
回复
完全看不懂
zzyong00 2009-07-06
  • 打赏
  • 举报
回复
mark
神马都能聊 2009-07-06
  • 打赏
  • 举报
回复
(1)如果你把Sub Main设为启动对象并按F5运行。观察几个debug输出的顺序,可以看到Set Nothing之后ccc的析构函数并未被调用,甚至ccc的属性仍然能够被访问,析构函数是直到应用终结才被调用的。
(2)如果只是在立即窗口测试Main,那析构函数压根儿就不会被执行。

-------------------------------------------------------------------------------
Set Nod.Parent = Me ,析构无法执行就是因为这里增加了一次node对tree的引用,所以你必须在孩子的析构里面解除引用。所以释放ccc之前,要先set ccc.node = nothing


'Class CAnnaNode

Public Parent As CAnnaTree



Private Sub Class_Terminate()
Set Parent = Nothing
End Sub



Public Sub main()
Dim ccc As New CAnnaTree

Set ccc.Nod = Nothing
Set ccc = Nothing
End Sub

代码修改类似
神马都能聊 2009-07-06
  • 打赏
  • 举报
回复
(1)如果你把Sub Main设为启动对象并按F5运行。观察几个debug输出的顺序,可以看到Set Nothing之后ccc的析构函数并未被调用,甚至ccc的属性仍然能够被访问,析构函数是直到应用终结才被调用的。
(2)如果只是在立即窗口测试Main,那析构函数压根儿就不会被执行。

-------------------------------------------------------------------------------
Set Nod.Parent = Me ,析构无法执行就是因为这里增加了一次node对tree的引用,所以你必须在孩子的析构里面解除引用。所以释放ccc之前,要先set ccc.node = nothing
神马都能聊 2009-07-06
  • 打赏
  • 举报
回复
我说的意思是这样:
1.

dim newfrm as form1
set newfrm = new form1
set newfrm = nothing
debug.print newfrm.name '出错,因为newfrm此时没有初始化。

2.

dim newfrm as new form1
set newfrm = nothing '此时对象被释放
debug.print newfrm.name '因为使用了new定义对象,所以这一句代码的作用类似于set newfrm = new form1

slowgrace 2009-07-06
  • 打赏
  • 举报
回复
TO AC,没看懂你在说啥
神马都能聊 2009-07-06
  • 打赏
  • 举报
回复
[Quote=引用 35 楼 slowgrace 的回复:]
Public Sub main()
Dim ccc As New CAnnaTree
' Dim ccc As CAnnaTree
' Set ccc = New CAnnaTree
Set ccc = Nothing
Debug.Print ccc.TreName
Debug.Print "set nothing", Time
End Sub
[/Quote]
使用new定义,这样的语句会重新激活对象
Set ccc = Nothing
Debug.Print ccc.TreName
slowgrace 2009-07-06
  • 打赏
  • 举报
回复
33楼后半部分说了TearDown的方法;之后还说了用全局调试集合的方法帮助调试,倒是个不错的办法,就是有点麻烦,你们平常这么做么?
slowgrace 2009-07-06
  • 打赏
  • 举报
回复
另外,这里有一个奇怪的现象。

如果把Main的代码替换成下面这样,Set Nothing之后ccc就不再是可用的了,虽然此时ccc的析构函数并没有被执行:

'Module 1

Public Sub main()
' Dim ccc As New CAnnaTree
Dim ccc As CAnnaTree
Set ccc = New CAnnaTree
Set ccc = Nothing
Debug.Print ccc.TreName '这一行就会报错
Debug.Print "set nothing", Time
End Sub


slowgrace 2009-07-06
  • 打赏
  • 举报
回复
to lyserver and Modest, 看这个例子就知道MSDN所言非虚:

'Class CAnnaTree

Public Nod As CAnnaNode
Public TreName As String

Private Sub Class_Initialize()
Set Nod = New CAnnaNode
Set Nod.Parent = Me
TreName = "throat"
End Sub

Private Sub Class_Terminate()
Debug.Print "Class_Terminate", Time
Set Nod.Parent = Nothing
End Sub

'Class CAnnaNode

Public Parent As CAnnaTree


'Module 1

Public Sub main()
Dim ccc As New CAnnaTree
' Dim ccc As CAnnaTree
' Set ccc = New CAnnaTree
Set ccc = Nothing
Debug.Print ccc.TreName
Debug.Print "set nothing", Time
End Sub



(1)如果你把Sub Main设为启动对象并按F5运行。观察几个debug输出的顺序,可以看到Set Nothing之后ccc的析构函数并未被调用,甚至ccc的属性仍然能够被访问,析构函数是直到应用终结才被调用的。
(2)如果只是在立即窗口测试Main,那析构函数压根儿就不会被执行。
slowgrace 2009-07-06
  • 打赏
  • 举报
回复
To zzyong:

之所以你引用的代码实际上会有效地调用析构函数,是因为先后两次Set Nothing,有效地把ccc的引用计数减为0了。
slowgrace 2009-07-06
  • 打赏
  • 举报
回复
回39楼AisaC:这回明白你的意思了。查了MSDN,“如果使用 New 来声明对象变量,则在第一次引用该变量时将新建该对象的实例”。
slowgrace 2009-07-06
  • 打赏
  • 举报
回复
你的理解貌似不对。你可以结合34楼的代码实际试试:)
zzyong00 2009-07-06
  • 打赏
  • 举报
回复
[Quote=引用 50 楼 slowgrace 的回复:]
回41楼 AisaC: 

(1)按照你的代码,即使不在CAnnaNode的析构函数里Set Parent = Nothing,CAnnaTree的析构函数也会被有效调用。

(2)甚至改成下面这样,也能有效调用CAnnaTree的析构函数

VB code'Module 1PublicSub main()Dim cccAsNew CAnnaTree' Dim ccc As CAnnaTree
' Set ccc = New CAnnaTreeSet ccc=NothingSet ccc.Nod=Nothing
Debug.Print ccc.TreName
Debug.Print"set nothing",TimeEnd Sub
[/Quote]
这样做,在对象生存期间,CAnnaTree的析构函数并没有被调用!只有在main结束后才调用CAnnaTree的析构函数,如果改为:
Dim ccc As New CAnnaTree
Private Sub Form_Load()
' Dim ccc As CAnnaTree
' Set ccc = New CAnnaTree
Set ccc = Nothing
Set ccc.Nod = Nothing

Debug.Print ccc.TreName
Debug.Print "set nothing", Time
End Sub

ccc真成了孤儿了,只有程序结束才能释放。
加载更多回复(35)

742

社区成员

发帖
与我相关
我的任务
社区描述
VB 版八卦、闲侃,联络感情地盘,禁广告帖、作业帖
社区管理员
  • 非技术类社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧