2012-04-09 35 views
8

Tôi sẽ tạo một GUI sẽ có bộ điều khiển được tạo động với các sự kiện được gán cho chúng. Tôi sẽ cần phải thêm và loại bỏ các điều khiển đó trong thời gian chạy. Nó sẽ giống như thế này:biện pháp phòng ngừa để thực hiện để ngăn chặn rò rỉ bộ nhớ do xử lý sự kiện thêm

FlowLayoutPanel.Controls.Clear(); 
<< add new controls, assigning Click events with += >> 

Tôi đã nghe nói rằng giao xử lý sự kiện với + = có thể gây rò rỉ bộ nhớ (specificly hơn, bộ nhớ sẽ không được trả tự do cho đến khi ứng dụng đã thoát). Tôi muốn tránh điều này. Tôi biết tôi có thể viết một số chức năng như ở đây How to remove all event handlers from a control để tìm tất cả các trình xử lý sự kiện và xóa chúng nhưng có vẻ rất phức tạp.

Có cách nào khác không? Việc gọi Dispose có giúp xóa các trình xử lý sự kiện đó không? Bạn có thể phá hủy các đối tượng để buộc bộ nhớ của họ được giải thoát như trong C/C++ không?

Cảm ơn!

PS: Vấn đề là, tôi không biết sự kiện nào cần tách ra. Tôi sẽ tạo nhiều nhãn và thêm các loại sự kiện onclick khác cho họ. Khi thời gian của mình để làm sạch bảng điều khiển bố cục luồng, không có cách nào để biết trình xử lý sự kiện nào được đính kèm với nhãn nào.

Đây là mã ví dụ (_flowLP là một FlowLayoutPanel) - chức năng Refresh() này được chạy nhiều lần trước khi ứng dụng thoát.

private void Refresh() 
    { 
     Label l; 
     Random rnd = new Random(); 

     // What code should i add here to prevent memory leaks 
     _flowLP.Controls.Clear(); 

     l = new Label(); 
     l.Text = "1"; 
     if (rnd.Next(3) == 0) l.Click += Method1; 
     if (rnd.Next(3) == 0) l.Click += Method2; 
     if (rnd.Next(3) == 0) l.Click += Method3; 
     _flowLP.Controls.Add(l); 

     l = new Label(); 
     l.Text = "2"; 
     if (rnd.Next(3) == 0) l.Click += Method1; 
     if (rnd.Next(3) == 0) l.Click += Method2; 
     if (rnd.Next(3) == 0) l.Click += Method3; 
     _flowLP.Controls.Add(l); 

     l = new Label(); 
     l.Text = "3"; 
     if (rnd.Next(3) == 0) l.Click += Method1; 
     if (rnd.Next(3) == 0) l.Click += Method2; 
     if (rnd.Next(3) == 0) l.Click += Method3; 
     _flowLP.Controls.Add(l); 

     l = new Label(); 
     l.Text = "4"; 
     if (rnd.Next(3) == 0) l.Click += Method1; 
     if (rnd.Next(3) == 0) l.Click += Method2; 
     if (rnd.Next(3) == 0) l.Click += Method3; 
     _flowLP.Controls.Add(l); 

     l = new Label(); 
     l.Text = "5"; 
     if (rnd.Next(3) == 0) l.Click += Method1; 
     if (rnd.Next(3) == 0) l.Click += Method2; 
     if (rnd.Next(3) == 0) l.Click += Method3; 
     _flowLP.Controls.Add(l); 

     l = new Label(); 
     l.Text = "6"; 
     if (rnd.Next(3) == 0) l.Click += Method1; 
     if (rnd.Next(3) == 0) l.Click += Method2; 
     if (rnd.Next(3) == 0) l.Click += Method3; 
     _flowLP.Controls.Add(l); 
    } 
+0

Về chỉnh sửa: Nếu không thể xác định phương thức được thêm vào danh sách yêu cầu của sự kiện, bạn có thể giữ các đại biểu đã được thêm vào trình xử lý sự kiện trong bộ sưu tập ở đâu đó và sử dụng nó để xóa đại biểu khi thời gian đến. Hoặc, nếu bạn biết bạn đang xóa hoàn toàn danh sách yêu cầu của sự kiện, chỉ cần xóa chúng hoàn toàn. – phoog

Trả lời

3

Điều này chủ yếu là lo lắng khi bạn đính kèm người tiêu dùng sự kiện sống ngắn hơn vào nhà sản xuất sự kiện còn sống lâu hơn. Nếu họ có cuộc sống tương tự hoặc ngược lại với những gì tôi mô tả, thì đó không phải là vấn đề.

Trong trường hợp bạn lo lắng về điều này, chỉ cần sử dụng - = để tách khỏi sự kiện. Điều đó loại bỏ các tham chiếu được tạo ra bởi tập tin đính kèm và giúp tránh loại vấn đề bộ nhớ.

Chỉnh sửa: Vì các nhận xét đang nhận được một chút thời gian dài, tôi sẽ đăng một số câu hỏi tiếp theo tại đây. Khi bạn đính kèm vào một sự kiện, những gì bạn đang làm là treo một tham chiếu đến chính bạn trên nhà cung cấp sự kiện. Vì vậy, ví dụ, nếu bạn có một lớp Clock với một sự kiện StrikesMidnight và bạn đăng ký sự kiện đó từ một lớp được gọi là Bedtime, cơ chế thực tế của Bedtime nói clock.StrikesMidnight += this.HandleMidnight; là bạn chỉ định đồng hồ tham chiếu đến chính mình. Nó giống như Clock có một thuộc tính đối tượng và bạn nói clock.ObjectProperty = this;

Vì vậy, trong trường hợp lớp Bedtime tồn tại trong thời gian ngắn và nằm ngoài phạm vi, Giờ đi ngủ xuất hiện, tự tham chiếu trên đồng hồ và sau đó đi ra khỏi phạm vi. Vấn đề là, Clock vẫn có một tham chiếu đến nó, vì vậy mặc dù nó nằm ngoài phạm vi, Garbage Collector sẽ không thu thập Bedtime.

....

Đó là nền tảng. Trong trường hợp của bạn, bạn đang tạo một nhãn và đính kèm một tham chiếu tới chính nó (thông qua trình xử lý "MethodX"). Khi làm mới được gọi, bạn xóa danh sách các nhãn của bạn (có nghĩa là chúng đi ra khỏi phạm vi). Họ đi ra khỏi phạm vi và họ có tham chiếu đến lớp học của bạn thông qua xử lý MethodX của nó, nhưng vì vậy những gì? Họ có tài liệu tham khảo không ngăn cản họ được GC'ed. Không ai đang giữ tài liệu tham khảo cho họ trong mã của bạn, do đó, GC sẽ làm công việc của họ trên chúng và bạn sẽ không bị rò rỉ bộ nhớ.

+1

Nhưng tôi không biết sự kiện nào để tách ra! Tôi phải làm gì? – Istrebitel

+1

Nó chỉ là nghịch đảo của những gì bạn đang làm với các câu lệnh + = của bạn. Mỗi lần bạn đính kèm một trình xử lý sự kiện cục bộ vào sự kiện của một cộng tác viên, hãy nhớ tách ra với - = khi bạn không còn cần phải biết về sự kiện đang được nâng lên. Bạn có thể làm điều này trong một phương thức vứt bỏ() nếu bạn muốn, mặc dù điều đó là không cần thiết. –

+0

có nhưng tôi không biết phương pháp để tách ra. nói rằng, tôi có 10 phương pháp, một trong số đó được gắn vào nhãn thứ 5 của 20 một số thời gian trước đây dựa trên các điều kiện tại thời điểm đó. Bây giờ tôi cần phải loại bỏ tất cả các nhãn từ flowlayoutpanel. Làm thế nào để tôi biết được 10 trình xử lý sự kiện nào mà tôi phải làm - =? – Istrebitel

0

Tất cả các điều khiển sẽ được dọn dẹp bởi bộ thu gom rác miễn là bạn vứt bỏ biểu mẫu có chứa.

Đăng ký sự kiện trên các điều khiển sẽ không giữ quyền kiểm soát còn sống, bởi vì điều khiển có tham chiếu đến đại biểu xử lý; đại biểu không có tham chiếu đến điều khiển.

Tình huống trong đó đăng ký sự kiện sẽ giữ cho kiểm soát không bị xóa, là nơi một số mã trong điều khiển đăng ký sự kiện bên ngoài mẫu biểu mẫu được chứa. Ví dụ: nếu hộp tổ hợp tùy chỉnh đăng ký một sự kiện trên một lớp tĩnh để cho phép kiểm soát biết khi nào danh sách tùy chọn của nó sẽ được cập nhật. Nếu điều khiển tùy chỉnh không bỏ qua sự kiện này, nó sẽ được tham chiếu bởi thanh ghi sự kiện các lớp tĩnh trong suốt thời gian của ứng dụng. Vì điều khiển sẽ có tham chiếu đến vùng chứa của nó (và vân vân) toàn bộ biểu mẫu có thể sẽ nằm trong bộ nhớ. Trong trường hợp như vậy, điều khiển sẽ không muốn sự kiện trong phương thức Vứt bỏ của nó. Việc đăng ký các sự kiện trên các lớp tĩnh hoặc các lớp cá thể tồn tại lâu dài phải luôn luôn nâng cao một lá cờ đỏ và được bỏ đi một cách rõ ràng khi không còn cần thiết nữa. Trong các hình thức, miễn là tất cả các sự kiện của bạn được kết nối với các phương pháp thể hiện trên lớp biểu mẫu hoặc trên các đối tượng có phạm vi được điều khiển bởi cá thể biểu mẫu, thì các điều khiển sẽ được dọn sạch khi biểu đồ đối tượng biểu mẫu rễ đi ra khỏi phạm vi. Chỉ cần cẩn thận rằng các lớp bên ngoài không giữ một tham chiếu đến biểu mẫu sau khi nó không còn cần thiết nữa.

+0

Nói cách khác, nếu một đối tượng nằm ngoài phạm vi có phương thức của đối tượng khác được subsctibed cho sự kiện của nó, nó sẽ được xử lý và GCed tốt, nhưng nếu một đối tượng nằm ngoài phạm vi thì một số phương thức của nó đã đăng ký sự kiện của đối tượng, sau đó nó sẽ không được GCed? – Istrebitel

+0

Điều đó khá chính xác. Họ sẽ vẫn nhận được rác thu thập nếu đối tượng mà họ đang đăng ký các sự kiện trên sẽ đủ điều kiện để thu thập. Thông tư tham chiếu giữa các đối tượng khác đủ điều kiện để được thu thập sẽ không giữ cho chúng khỏi bị thu thập. –

0

Đề xuất đầu tiên của tôi sẽ không được tối ưu hóa trước. Hãy chắc chắn rằng đây là một vấn đề trước khi bạn cố gắng giải quyết nó.

Liên quan đến Vứt bỏ(): Vứt bỏ nên CHỈ được sử dụng để giải phóng tài nguyên không được quản lý trước khi đối tượng thu gom rác. Xem http://msdn.microsoft.com/en-us/library/system.idisposable.aspx để biết thêm thông tin.

Để ngăn trình xử lý sự kiện giữ các tham chiếu đến các điều khiển của bạn, bạn sẽ cần phải có các điều khiển hủy đăng ký khỏi tất cả các trình xử lý sự kiện mà họ đăng ký khi chúng được tạo. GỢI Ý: Nếu bạn đang thêm trình xử lý sự kiện thông qua GUI, hãy điều tra mã được tạo tự động để xem những gì bạn cần hủy đăng ký trước khi gọi FlowLayoutPanel.Controls.Clear(). Một cách làm sạch (-er?) Là tạo điều khiển riêng của bạn, kế thừa từ điều khiển mong muốn và thêm phương thức `Cleanup() 'để hủy đăng ký từ bất kỳ trình xử lý sự kiện nào đã được đăng ký (bạn nên biết sự kiện nào các trình xử lý đã được đăng ký - hoặc vì bạn đã viết mã hoặc được tạo cho bạn).

+0

Không, như tôi đã nói, tôi đang làm điều này tự động, do đó, không thông qua GUI. – Istrebitel

Các vấn đề liên quan