面向对象的LotusScript,Notes中的垃圾回收之LotusS

2019-09-04 23:23 来源:未知

Java使得垃圾回收的概念不再只有环保人士熟悉,还成为程序员的必修课。我们先来看看它的定义:垃圾回收是一种自动内存管理的形式。回收器监测程序中的对象,当它们不再被需要时,收回其占用的内存。与之相对的是程序员人为指定何时销毁对象。

上次提到使用面向对象的LotusScript的好处,这里再给出两个样例。

实际上,垃圾回收的产生很自然。在函数中声明的各种基本数据类型的变量都是在栈(stack)里创建,退出函数时占用的内存因为栈弹出被自动回收。既不需要程序员注意,计算机也不必特殊处理。但是栈作为实现函数调用的机制,只适合于保存较小的数据。如果是复杂的数据类型(体积较大),比如一个对象,就会被创建在其他专门的内存区域,在Java里就是堆(heap),栈里只保存引用这个对象的指针。那么当函数退出,其中的局部变量失效时,它们引用的对象仍然滞留在内存中。像C++这样的语言就要求程序员手工销毁对象,实践证明这项工作既繁琐又容易出错。人不想做,就想到让计算机代劳,而干繁琐又容易出错的活正是机器的专长——细致和不知疲倦。于是垃圾回收技术就诞生了。

 

回到Lotus Notes中来,垃圾回收也一直在发挥作用,只是进行得悄无声息,以至于大部分人都没有注意到它。在LotusScript中,我们使用Notes对象,只管创建,它们退出作用域之后占用的内存都是被自动回收的。这实际上很自然,因为LotusScript所源于的VBA使用的COM对象就采用了基于引用计数的垃圾回收。

DialogBox是NotesUIWorkspace的一个方法,用于显示一个指定表单的对话框,在开发中很有用。不过对话框繁多的设置都通过方法的参数传递,难记难用。下面就用一个自定义类包装了DialogBox的功能,默认情况下只需要传入一个表单或者文档参数,就可以调用Show()方法显示一个对话框。对话框的显示可以通过设置属性调整。原有的参数按照其意义进行了分类,可以更方便地设置。比如Fit属性可以控制HorizonalFit和VerticalFit设置,AllowUpdate属性可以控制NoFieldUpdate和NoNewFields。如果必要,也可以通过公开的字段更精细地修改原有的参数。

下面就用几个例子来测试一下读者诸君对LotusScript里垃圾回收特性的了解。

 

例子1

[vb]  

[vb]  

%REM  

Function GetConfigDoc(key As String) As NotesDocument  

    Class DialogBox  

    Dim s As New NotesSession  

    Description: Wraps the ws.Dialogbox() method.  

    'Get the db containing the config documents  

%END REM  

    Dim server As String, path As String  

Public Class DialogBox  

    server="SERVER"  

    Public FormName As String   

    path="abc.nsf"   

    Public HorizonalFit As Boolean  

    Dim db As NotesDatabase  

    Public VerticalFit As Boolean  

    Set db=s.GetDatabase(server, path)  

    Public NoCancel As Boolean  

    Set GetConfigDoc=db.Getview("vwConfig").Getdocumentbykey(key, True)   

    Public NoNewFields As Boolean  

End Function  

    Public NoFieldUpdate As Boolean  

Sub Initialize  

    Public  ReadOnly As Boolean  

    Dim doc As NotesDocument  

    Public Title As String  

    Set doc=GetConfigDoc("No")  

    Public Document As NotesDocument  

    Print doc.Values(0)  

    Public SizeToTable As Boolean  

End Sub  

    Public NoOKCancel As Boolean  

例子2

    Public OKCancelAtBottom As Boolean  

[vb]  

      

Dim entry As NotesViewEntry   

    %REM  

Set entry=nav.Getchild(personEntry) 'nav is a NotesViewNavigator  

        Property Set Fit  

Dim i As Integer   

        Description: Wraps the HorizonalFit and VerticalFit fields  

For i=1 To entry.Siblingcount-1  

    %END REM  

    entry.Document.Type="Arrival"  

    Property Set Fit As Boolean  

    Call entry.Document.Save(True, False)  

        me.HorizonalFit=Fit  

    Set entry=nav.Getnextsibling(entry)  

        me.VerticalFit=Fit  

Next  

    End Property  

诸君目测一下上述两段代码运行结果如何?

      

实际结果是例子1中的代码会报错Object variablenot set。例子2中的代码运行后完全没作用。如果在工作中遇到了这样的情况,你可能会抓破脑袋,最后只能悻悻地换一种写法。语法没有任何错误,更让人懊恼的是在调试状态下逐行运行也看不出什么异常。原因就出在LotusScript的垃圾回收。

    %REM  

我们再来看一段代码:

        Property Set AllowUpdate  

[vb]  

        Description: Wraps the NoFieldUpdate and NoNewFields fields  

Set view = db.GetView("$all")   

    %END REM  

Set doc = view.GetFirstDocument()   

    Property Set AllowUpdate As Boolean  

While Not (doc Is Nothing)   

        me.NoFieldUpdate=Not AllowUpdate  

    ' do something...   

        me.NoNewFields=Not AllowUpdate  

    Set doc = view.GetNextDocument(doc)   

    End Property  

Wend  

      

这是我们遇到过无数次的片段,看上去很简单。即使有50,000条文档,程序也能运行得很稳定,占用的资源不会比50条文档的时候多。我们来仔细看看,在while的循环体中,doc变量被不断指向新的文档对象,旧的文档对象哪里去了?这不正是自动垃圾回收应用的场合。LotusScript解释器自动销毁了这些对象,对程序员来说完全是透明的。一切都很好,对吧?那为什么例子1和例子2的结果会不正常呢?这是因为LotusScript的垃圾回收有以下几个特点:

    %REM  

1.每一行代码运行完之后都会进行,以检查是否有对象不再有变量引用。函数退出时则回收所有本地变量指向的对象。

        Property Set DefaultButton  

2.某个对象只要不再有局部变量直接指向它,就会被回收,即使有其他对象通过属性关联到它。

        Description: Wraps the NoCancel and NoOKCancel fields  

3.回收一个对象时,它包含的其他对象都会被回收。包含指的是一个对象由另一个对象的方法或属性获得,比如从一个数据库对象获取某个视图,从视图取得文档,从文档得到域对象。因为Notes对象的“树”状关系,当一个数据库对象被销毁时,属于它的视图、文档等各种对象都会被回收。而当一个NotesSession被回收时,此次会话涉及的所有对象也都会被销毁。

    %END REM  

可以看出这样的垃圾回收机制简单有效,但是做得过分了,特别是第一点和第三点同时发挥作用。现在我们就可以解释例子1和例子2的实际运行结果。在例子1里,函数GetConfigDoc返回一个文档对象,但是随即包含这个文档的数据库对象就被回收,根据上面的第三点特性,该文档对象也被回收,因而文档变量无法被访问。在例子2里,NotesViewEntry指向的文档对象修改这一行执行完之后,垃圾回收运行,因为没有变量指向,在下一行调用保存之前,该文档对象就被销毁了。调用保存方法时,文档对象是重新从NotesViewEntry获得的。在实际编程中,我们还有可能不小心掉进这个过分强大的垃圾回收的陷阱里。幸好,了解了它的特点后,很容易就可以避开,只要稍微改动一下代码。对于例子1的情况,我们可以直接用函数返回配置文档中的字段值比如字符串和数字,或者在调用它的函数中,先创建一个数据库变量指向配置数据库。在例子2里,我们不能省去声明一个文档变量的步骤,这样因为有变量引用,文档对象就不会被回收:

    Property Set DefaultButton As Boolean  

[vb]  

        me.NoCancel=Not DefaultButton  

Dim doc As NotesDocument  

        me.NoOKCancel=Not DefaultButton  

Dim entry As NotesViewEntry 'a record entry  

    End Property  

Set entry=nav.Getchild(personEntry) 'the first record  

      

Dim i As Integer   www.2cto.com

    %REM  

For i=1 To entry.Siblingcount-1  

    Sub New  

    Set doc=entry.Document  

    Description: The argument info is either a string of a form name or a document to be shown  

    doc.Type="Arrival"  

    %END REM  

    Call doc.Save(True, False)  

    Public Sub New(info As Variant)  

    Set entry=nav.Getnextsibling(entry)  

        'stop  

Next  

        'Default values  

...

        me.HorizonalFit=False  

        me.VerticalFit=False  

        me.NoCancel=False  

        me.NoFieldUpdate=False  

        me.NoNewFields=False  

        me.NoOKCancel=False  

        me.OKCancelAtBottom=False  

        me.ReadOnly=False  

        me.SizeToTable=False  

        me.Title="Lotus Notes"  

          

        Select Case TypeName(info)  

            Case "NOTESDOCUMENT"  

                Set me.Document=info  

                me.FormName=info.Form(0)  

            Case "STRING"  

                'info is the form name  

                me.FormName=info  

                Set me.Document=CreateDoc(me.FormName)  

        End Select  

  

    End Sub   

      

    %REM  

    Function Show  

    Description: Show the dialogbox.  

    %END REM  

    Public Function Show() As Boolean  

        Dim ws As New NotesUIWorkspace  

        Show = ws.Dialogbox(Me.FormName, Me.HorizonalFit, Me.VerticalFit, Me.NoCancel, Me.NoNewFields, Me.NoFieldUpdate, Me.ReadOnly, Me.Title, Me.Document, Me.SizeToTable, Me.NoOKCancel, Me.OKCancelAtBottom)  

    End Function  

End Class  

下面这个自定义类对Picklistcollection也做了类似的包装。默认情况下打开当前数据库的某个视图,分别通过SelectDocs和SelectDoc进行多选和单选。

 

[vb]  

%REM  

    Class PickBox  

    Description: Wraps the ws.Picklistcollection() method.  

%END REM  

Public Class PickBox  

    Private ws As NotesUIWorkspace  

    Private s As NotesSession  

    Private db As NotesDatabase  

      

    Public MultiSelect As Boolean  

    Public Server As String  

    Public DBPath As String  

    Public Title As String  

    Public Prompt As String  

    Public SingleCategory As String  

      

    %REM  

        Sub New  

        Description: Comments for Sub  

    %END REM  

    Sub New()  

        Set ws=New NotesUIWorkspace  

        Set s=New NotesSession  

        Set db=s.Currentdatabase  

        'Default to open a view in the current db.  

        Server=db.Server  

        DBPath=db.Filepath  

        Title="Lotus Notes"  

        Prompt="Please select the document(s)."  

    End Sub  

      

    %REM  

        Function SelectDocs  

        Description: MultiSelect, return a NotesDocumentCollection  

    %END REM  

    Public Function SelectDocs(viewName As String) As NotesDocumentCollection  

        Set SelectDocs = ws.Picklistcollection(PICKLIST_CUSTOM, MultiSelect, Server, DBPath, ViewName, Title, Prompt, Singlecategory)  

    End Function  

      

    %REM  

        Function SelectDoc  

        Description: Return a single NotesDocument.  

    %END REM  

    Public Function SelectDoc(viewName As String) As NotesDocument  

        me.MultiSelect=False   

        Set SelectDoc=me.SelectDocs(viewName).Getfirstdocument()  

    End Function  

End Class  

下面是使用上述两个类的简单样例:

 

[vb]  

Dim dlgbox As New DialogBox("formName")  

With dlgbox  

    .DefaultButton=True  

    .AllowUpdate=False   

End With  

Dim result As Boolean  

result=dlgbox.Show()  

  

Dim pb As New PickBox  

Dim doc As NotesDocument  

Set doc=pb.SelectDoc("viewName")  

 

DialogBox是NotesUIWorkspace的一个方法,用于显示一个指定表单的对话框,在开...

TAG标签:
版权声明:本文由990888藏宝阁发布于前端代码,转载请注明出处:面向对象的LotusScript,Notes中的垃圾回收之LotusS