Có cần thiết phải Set đối_tượng = Nothing trước khi kết thúc hàm/thủ tục trong VBA không? (1 người xem)

Người dùng đang xem chủ đề này

Tôi tuân thủ nội quy khi đăng bài

nguyendang95

Thành viên thường trực
Tham gia
25/5/22
Bài viết
214
Được thích
185
Chắc hẳn nhiều người khi tiếp xúc với VBA đã từng có thắc mắc như vậy, có người cho rằng nên Set Nothing trước khi kết thúc chương trình bởi vì VBA không tự động giải phóng bộ nhớ, nhưng cũng có người tin rằng chẳng cần phải mất công Set Nothing làm gì bởi vì VBA đủ thông minh để tự dọn dẹp bộ nhớ sau khi kết thúc chương trình. Trăm nghe không bằng một thấy, để tìm câu trả lời cho câu hỏi này, chúng ta hãy làm một bài thử nghiệm nhỏ. Bài viết này sử dụng mã viết bằng Visual C++, tạo một đối tượng COM tên là Employee đơn giản bằng ATL Project, đối tượng này có duy nhất một thuộc tính kiểu chuỗi tên là FullName như hình dưới đây:

1776480055507.png

Sau đây là hai mã VBA để chạy thử:
Mã:
Public obj As CollectionObjectsLib.Employee

Private Sub Test()
    Set obj = New CollectionObjectsLib.Employee
    obj.FullName = "AAA"
End Sub

Mã:
Private Sub Test2()
    Dim obj As CollectionObjectsLib.Employee
    Set obj = New CollectionObjectsLib.Employee
    obj.FullName = "AAA"
End Sub

Mở Visual Studio, biên dịch dự án ra DLL rồi chạy trong chế độ gỡ lỗi (Debug), sau đó đặt điểm ngắt (breakpoint) ở phương thức FinalRelease của Employee.

1776480329029.png

Bắt đầu lần lượt chạy hai thủ trên. Khi chạy đến End Sub, trình gỡ lỗi của Visual Studio nhảy đến điểm ngắt trong phương thức FinalRelease, chứng tỏ VBA đã gọi phương thức Release để giải phóng đối tượng khỏi bộ nhớ (nên nhớ phương thức FinalRelease chỉ được gọi khi RefCount, hay đếm tham chiếu, của đối tượng bằng 0).

1776480696680.png

Cửa sổ Immediate ghi lại thông tin như sau, quá trình từ lúc khởi tạo đối tượng Employee (khởi tạo IClassFactory, IUnknown rồi mới cho ra IEmployee, giải phóng IClassFactory và IUnknown sau khi đã khởi tạo đối tượng xong), khởi tạo IDispatch từ IEmployee để thiết lập thuộc tính FullName của đối tượng Employee, xong việc rồi thì giải phóng IDispatch, cuối cùng là giải phóng đối tượng Employee (IEmployee) sau khi thủ tục kết thúc:

1776480769962.png

Kết luận: Không cần thiết phải Set đối_tượng Nothing trước khi kết thúc hàm/thủ tục.
 

File đính kèm

/(/ên Set Nothing trước khi kết thúc chương trình là còn bởi vì
→ Là thói đời của những nhà lập trình thích tiêu tốn 1 khoản tiền điện để xây tính cẩn trọng trong lúc này hay lúc khác!
→ Là hướng dẫn cho lớp sau 'khóa đuôi' sau 1 chiến trận!
→ Là sẽ không sợ ai bắt tội 'lãng fí' hay tham những ( :D )

. . . . .


Không nhất thiết phải Set (gán Nothing) cho biến đối tượng trước khi kết thúc một macro hay Function trong VBA, nhưng việc này lại là một thói quen tốt trong nhiều trường hợp.

Vì sao?​

  • Khi macro kết thúc, tất cả biến cục bộ sẽ tự động được giải phóng khỏi bộ nhớ. Do đó, về mặt kỹ thuật, bạn không cần phải gán Nothing.
  • Tuy nhiên, nếu bạn đang làm việc với đối tượng ngoài(ví dụ: Workbook, Worksheet, Range, Recordset, FileSystemObject…), thì việc gán Nothing giúp:
    • Giải phóng tài nguyên ngay lập tức, không chờ đến khi VBA tự dọn dẹp.
    • Tránh tình trạng “treo” hoặc khóa file/ứng dụng (ví dụ: Excel không cho đóng workbook vì vẫn còn tham chiếu).
    • Giúp code rõ ràng, dễ bảo trì, và giảm nguy cơ rò rỉ bộ nhớ khi macro phức tạp hoặc chạy lâu.

Khi nào nên dùng?​

  • ✅ Với đối tượng COM hoặc đối tượng ngoài (ADO, FileSystemObject, Excel Application, Word Application…).
  • ✅ Khi bạn mở/đóng file hoặc kết nối dữ liệu, để chắc chắn rằng tài nguyên được giải phóng.
  • ❌ Với biến cục bộ đơn giản (String, Integer, Double, Boolean…), không cần gán gì cả.

Ví dụ:​

PHP:
Sub Demo()
    Dim wb As Workbook
    Set wb = Workbooks.Open("C:\Test.xlsx")
      ' Làm gì đó với wb...       '
    wb.Close SaveChanges:=False
    Set wb = Nothing   ' Giải phóng tham chiếu ngay
End Sub

Tóm lại: Không bắt buộc, nhưng nên làm khi làm việc với đối tượng ngoài để code an toàn và chuyên nghiệp hơn.

Bạn có muốn mình chia sẻ thêm một “best practice checklist” nhỏ cho việc quản lý biến đối tượng trong VBA không?
 
Lần chỉnh sửa cuối:
Upvote 0
Có mấy trường hợp bạn có test chưa?
- Biến đối tượng được khai báo Global và nằm ở cấp module.
- Biến đối tượng là một ứng dụng khác được gọi trong code. Ví dụ: trong Access gọi đối tượng là Excel.Application.
 
Upvote 0
Có mấy trường hợp bạn có test chưa?
- Biến đối tượng được khai báo Global và nằm ở cấp module.
- Biến đối tượng là một ứng dụng khác được gọi trong code. Ví dụ: trong Access gọi đối tượng là Excel.Application.
1. Ý bạn là trường hợp này?

Mã:
Public (hoặc Private) obj As CollectionObjectsLib.Employee

Private Sub Test2()
    'Dim obj As CollectionObjectsLib.Employee
    Set obj = New CollectionObjectsLib.Employee
    obj.FullName = "AAA"
End Sub

Trường hợp này Refcount là 2, với +1 từ việc obj được khai báo ở trên cùng module (Public, Private hay Dim) và +1 từ lệnh Set, cho nên khi Release (End Sub) thì RefCount giảm về 1, tức là obj vẫn còn lưu lại ở bộ nhớ, cái này là do thiết kế của VBA nhằm đảm bảo đối tượng có thể được gọi từ bất kỳ module (public) hoặc giữa các hàm/thủ tục trong module (private hoặc Dim). Tất nhiên trường hợp này bạn có thể chủ động Set Nothing để giảm Refcount về 0 để thật sự xóa đối tượng khỏi bộ nhớ nếu cần, nhưng theo tôi thấy không nên chia sẻ biến giữa các thủ tục/hàm và giữa các module kiểu này, thay vào đó nên truyền biến dưới dạng tham số cho các hàm/thủ tục để dễ quản lý.

1776520729427.png

Để ý khi chạy xong, nếu đổi tên biến obj thành obj1 hoặc obj2, v.v... thì VBA ngay lập tức giải phóng đối tượng theo tên biến cũ, hoặc khi thoát Excel thì VBA cũng dọn dẹp mấy biến kiểu này luôn.
2. Tất nhiên là Microsoft chắc hẳn phải nhắc người viết mã gọi phương thức Quit của đối tượng Application trước khi kết thúc chương trình, Set Nothing hay không trong trường hợp này chỉ là xóa bỏ đối tượng khỏi bộ nhớ chứ không xóa tài nguyên đi kèm theo đối tượng mà (dĩ nhiên là mong muốn lưu lại dữ liệu trước khi thoát thay vì đùng một phát thoát luôn làm mất luôn dữ liệu chưa lưu lại), thường thì trong tài liệu về mô hình người ta sẽ nói rõ nên gọi phương thức nào để giải phóng tài nguyên trước khi gọi Release của đối tượng.
 
Upvote 0
1776521082330.png

Copilot trả lời

Trong VBA, việc Set đối_tượng = Nothing trước khi kết thúc hàm/thủ tục không bắt buộc, nhưng có một số điểm đáng lưu ý:

  • Bộ nhớ tự động giải phóng: Khi một biến đối tượng (Object variable) ra khỏi phạm vi (scope) của hàm/thủ tục, VBA sẽ tự động giải phóng tham chiếu đó. Vì vậy, về mặt kỹ thuật, bạn không cần phải Set ... = Nothing.
  • Thói quen lập trình tốt: Tuy không bắt buộc, nhưng nhiều lập trình viên vẫn dùng Set ... = Nothing để:
    • Rõ ràng hóa việc kết thúc sử dụng đối tượng.
    • Giúp tránh lỗi khi vô tình tái sử dụng biến sau này.
    • Trong một số trường hợp đặc biệt (ví dụ: đối tượng COM, kết nối cơ sở dữ liệu, Excel Application khi làm việc với Automation), việc giải phóng sớm có thể giúp tránh rò rỉ bộ nhớ hoặc giữ tài nguyên bị khóa.
  • Khác biệt với biến thường: Với biến kiểu dữ liệu cơ bản (Integer, String, Double…), không có khái niệm = Nothing. Chỉ áp dụng cho biến đối tượng.

Tóm lại: Không cần thiết phải Set ... = Nothing trong mọi trường hợp, nhưng nếu bạn làm việc với đối tượng nặng (như Excel, Word, ADO Connection), thì việc chủ động giải phóng bằng Set ... = Nothing là một thói quen tốt để quản lý tài nguyên.


Hỏi lại nó
1776521158576.png
Đúng rồi, câu hỏi kiểu “không cần Set ... = Nothing” là hơi thiếu chính xác nếu áp dụng chung cho mọi tình huống.

  • Với biến đối tượng thông thường trong VBA, đúng là khi thoát khỏi phạm vi thì bộ nhớ sẽ tự giải phóng, nên về mặt kỹ thuật không bắt buộc.
  • Nhưng với đối tượng COM nặng như ADODB.Connection hay ADODB.Recordset, nếu bạn giữ tham chiếu lâu dài hoặc dùng chung nhiều hàm, thì việc chủ động Close và sau đó Set ... = Nothing là cần thiết để giải phóng tài nguyên, tránh rò rỉ bộ nhớ và tình trạng kết nối vẫn mở.

Vì vậy, nói “không cần” là sai nếu hiểu theo nghĩa tuyệt đối. Chính xác hơn phải nói: không bắt buộc trong mọi trường hợp, nhưng với đối tượng COM hoặc tài nguyên ngoài, nên chủ động Set ... = Nothing sau khi dùng xong.
 
Upvote 0

Bài viết mới nhất

Back
Top Bottom