2011-02-03 53 views
61

Có một đối tượng của lớp QNetworkReply. Có một khe (trong một số đối tượng khác) được kết nối với tín hiệu đã hoàn thành() của nó. Tín hiệu là đồng bộ (các tín hiệu mặc định). Chỉ có một chủ đề.Cách xóa và xóaLater hoạt động liên quan đến tín hiệu và vị trí trong Qt?

Tại một thời điểm nào đó, tôi muốn loại bỏ cả hai đối tượng. Không có thêm tín hiệu hoặc bất cứ điều gì từ họ. Tôi muốn họ đi. Vâng, tôi nghĩ, tôi sẽ sử dụng

delete obj1; delete obj2; 

Nhưng tôi có thể thực sự không? Thông số kỹ thuật cho ~ QObject nói:

Xóa QObject trong khi đang chờ sự kiện đang chờ phân phối có thể gây ra sự cố.

'Sự kiện đang chờ xử lý' là gì? Điều đó có nghĩa là khi tôi gọi số delete của mình, đã có một số 'sự kiện đang chờ xử lý' được phân phối và chúng có thể gây ra sự cố và tôi thực sự không thể kiểm tra xem có bất kỳ sự cố nào không?

Vì vậy, chúng ta hãy nói rằng tôi gọi:

obj1->deleteLater(); obj2->deleteLater(); 

Để được an toàn.

Nhưng, tôi có thực sự an toàn không? Các deleteLater thêm một sự kiện sẽ được xử lý trong vòng lặp chính khi kiểm soát được ở đó. Có thể có một số sự kiện đang chờ xử lý (tín hiệu) cho obj1 hoặc obj2 đã có, đang chờ xử lý trong vòng lặp chính trước khi deleteLater sẽ được xử lý? Điều đó sẽ rất không may. Tôi không muốn viết mã kiểm tra cho tình trạng 'hơi bị xóa' và bỏ qua tín hiệu đến trong tất cả các khe của tôi.

+3

Có vẻ như 'obj-> disconnect(); obj-> deleteLater(); 'là đúng cách để đi: – stach

Trả lời

59

Xóa QObjects thường là an toàn (ví dụ trong thực tế bình thường, có thể có những trường hợp bệnh lý tôi không nhận thức được atm), nếu bạn làm theo hai nguyên tắc cơ bản:

  • Chưa bao giờ xóa một đối tượng trong một khe hoặc phương thức được gọi trực tiếp hoặc gián tiếp bởi tín hiệu (đồng bộ, kiểu kết nối "trực tiếp") từ đối tượng cần xóa. Ví dụ: nếu bạn có một lớp Hoạt động với một tín hiệu Hoạt động :: đã hoàn thành() và một Trình quản lý khe :: operationFinished(), bạn không muốn xóa đối tượng hoạt động phát ra tín hiệu trong khe đó. Phương thức phát tín hiệu đã hoàn thành() có thể tiếp tục truy cập "this" sau khi phát ra (ví dụ: truy cập một thành viên), và sau đó hoạt động trên con trỏ "this" không hợp lệ.

  • Tương tự như vậy, không bao giờ xóa đối tượng trong mã được gọi đồng bộ từ trình xử lý sự kiện của đối tượng. Ví dụ. không xóa một SomeWidget trong SomeWidget :: fooEvent() hoặc trong các phương thức/khe mà bạn gọi từ đó. Hệ thống sự kiện sẽ tiếp tục hoạt động trên đối tượng đã bị xóa -> Crash.

Cả hai có thể được khôn lanh để theo dõi xuống, như những vết lùi thường trông lạ (Giống như vụ tai nạn khi truy cập một biến thành viên POD), đặc biệt là khi bạn đã phức tạp chuỗi tín hiệu/khe nơi một xóa có thể xảy ra một số bước xuống ban đầu được khởi tạo bởi một tín hiệu hoặc sự kiện từ đối tượng bị xóa.

Trường hợp này là trường hợp sử dụng phổ biến nhất cho deleteLater(). Nó đảm bảo rằng sự kiện hiện tại có thể được hoàn thành trước khi điều khiển trả về vòng lặp sự kiện, sau đó xóa đối tượng.Khác, tôi tìm thấy cách tốt hơn thường là trì hoãn toàn bộ hành động bằng cách sử dụng một kết nối xếp hàng đợi/QMetaObject :: invokeMethod (..., Qt :: QueuedConnection).

2

Theo như tôi biết, điều này chủ yếu là một vấn đề nếu các đối tượng tồn tại trong các chủ đề khác nhau. Hoặc có thể trong khi bạn đang thực sự xử lý tín hiệu.

Nếu không xóa QObject trước tiên sẽ ngắt kết nối tất cả các tín hiệu và vị trí và xóa tất cả các sự kiện đang chờ xử lý. Như một cuộc gọi để ngắt kết nối() sẽ làm.

13

Bạn có thể tìm thấy câu trả lời cho câu hỏi của bạn đọc về một trong những Delta Object Rules trong đó nêu này:

tín hiệu an toàn (SS).
Phải an toàn đối với các phương thức gọi trên đối tượng, bao gồm trình phá hủy, từ trong một khe được gọi bằng một trong các tín hiệu của nó.

Fragment:

Tại cốt lõi của nó, QObject hỗ trợ được xóa trong khi tín hiệu. Để tận dụng lợi thế của nó, bạn chỉ cần đảm bảo đối tượng của bạn không cố gắng truy cập bất kỳ thành viên nào của riêng mình sau khi bị xóa. Tuy nhiên, hầu hết các đối tượng Qt không được viết theo cách này và cũng không yêu cầu chúng phải là . Vì lý do này, nó là đề nghị bạn luôn gọi deleteLater() nếu bạn cần xóa một đối tượng trong một trong các tín hiệu của nó, vì tỷ lệ cược là ‘xóa’ sẽ chỉ làm hỏng ứng dụng.

Thật không may, không phải lúc nào cũng rõ ràng khi bạn nên sử dụng ‘delete’ so với deleteLater(). Tức là, không phải là luôn luôn rõ ràng rằng đường dẫn mã có nguồn tín hiệu . Thông thường, bạn có thể có một khối mã sử dụng 'xóa' trên một số đối tượng an toàn trong ngày hôm nay, nhưng tại một số thời điểm trong tương lai cùng một mã này kết thúc lên được gọi từ nguồn tín hiệu và đột nhiên ứng dụng của bạn đang gặp sự cố. Chỉ giải pháp chung cho vấn đề này là sử dụng deleteLater() mọi lúc, ngay cả nếu trong nháy mắt, có vẻ như không cần thiết.

Thông thường, tôi coi Quy tắc đối tượng Delta là bắt buộc đối với mọi nhà phát triển Qt. Đó là tài liệu đọc tuyệt vời.

+1

Nếu bạn theo liên kết đến DOR, bạn phải theo các liên kết trên trang đó để đọc thêm, ví dụ: theo liên kết đến 'Signal Safe'. Trang đầu tiên từ liên kết khó hiểu mà không có ngữ cảnh. (Tôi đang theo đuổi sự cố khi thoát bằng PyQt trên Windows, ứng dụng của tôi thậm chí không xóa bất kỳ đối tượng nào, nhưng tôi hy vọng liên kết tới DOR sẽ cung cấp thông tin chi tiết.) – bootchk

19

Hai dòng tiếp theo của tài liệu được giới thiệu của bạn cho biết câu trả lời.

Từ ~QObject,

Xóa một QObject trong khi chưa giải quyết được chờ đợi sẽ được chuyển giao có thể gây ra một vụ tai nạn. Bạn không được xóa trực tiếp QObject nếu nó tồn tại trong một luồng khác với chuỗi đang thực hiện. Sử dụng deleteLater() thay vào đó, điều này sẽ khiến vòng lặp sự kiện xóa đối tượng sau khi tất cả các sự kiện đang chờ xử lý đã được gửi đến nó.

Cụ thể là chúng tôi không được xóa khỏi các chủ đề khác. Vì bạn có một ứng dụng đơn luồng, nên an toàn để xóa QObject.

Khác, nếu bạn phải xóa nó trong môi trường nhiều luồng, hãy sử dụng deleteLater() để xóa QObject sau khi xử lý tất cả các sự kiện đã được thực hiện.

+5

Điều gì về kịch bản thứ hai của tôi? Có thể khe trong một đối tượng vẫn được gọi sau khi tôi gọi deleteLater trên nó? – stach