Xóa trình quan sát cho NSNotification trong Swift ở đâu?


83

Tôi nên xóa trình quan sát NSNotificationở đâu trong Swift, vì viewDidUnloaddealloc()không có sẵn?


ngày nay bạn không cần phải xóa chúng theo cách thủ công, trừ khi bạn đang sử dụng kiểu khối.
Fattie,

Câu trả lời:


71

Sử dụng phương pháp dưới đây có chức năng giống như dealloc.

deinit {
    // Release all resources
    // perform the deinitialization
}

Một deinitializer được gọi ngay lập tức trước khi một cá thể lớp được phân bổ. Bạn viết deinitializers với từ khóa deinit, tương tự như cách intializers được viết với từ khóa init. Deinitializers chỉ có sẵn trên các loại lớp.

Swift Deinitializer


12
Kể từ iOS 9, theo một câu trả lời bên dưới, các trình quan sát sẽ tự động bị xóa cho bạn trừ khi bạn đang sử dụng các trình quan sát dựa trên khối.
Crashalot

deinitPhương thức @Kampai cho ViewControllerA sẽ không được gọi khi nó sẽ đẩy ViewControllerB.
Anirudha Mahale

@AnirudhaMahale - Không, vì ViewControllerA vẫn nằm trong ngăn xếp của bộ điều khiển điều hướng. deinitđối với ViewControllerA sẽ chỉ được gọi khi nó không nằm trong ngăn xếp của bộ điều khiển điều hướng. Ví dụ: Chuyển sang rootViewController (nếu rootViewController không phải là ViewControllerA)
Kampai

@Kampai: Điều này sẽ không hoạt động như thể bạn đang thêm người quan sát trong bộ điều khiển chế độ xem. Có nhiều khả năng nó sẽ bị bắt theo chu kỳ lưu giữ và sẽ không gọi được deinit. Nơi lý tưởng để cuộc gọi sẽfunc viewDidDisappear(_ animated: Bool)
Bhanu Birani

@BhanuBirani: Bạn có thể vui lòng giải thích bất kỳ trường hợp nào mà bạn đang đề cập về "khả năng cao" không. Theo kinh nghiệm của tôi, tôi đã không phải đối mặt với bất kỳ.
Kampai

135

Kể từ iOS 9 (và OS X 10.11), bạn không cần phải tự mình xóa người quan sát , nếu bạn không sử dụng trình quan sát dựa trên khối. Hệ thống sẽ làm điều đó cho bạn, vì nó sử dụng các tham chiếu yếu cho người quan sát, nếu nó có thể.

Và nếu bạn đang sử dụng trình quan sát dựa trên khối, hãy đảm bảo rằng bạn tự nắm bắt yếu bằng cách sử dụng [weak self]trong danh sách nắm bắt của đóng và loại bỏ trình quan sát trong deinitphương pháp. Nếu bạn không sử dụng tham chiếu yếu cho bản thân, deinitphương thức (và do đó xóa người quan sát đó) sẽ không bao giờ được gọi vì Trung tâm thông báo sẽ giữ một tham chiếu mạnh đến nó vô thời hạn.

Có thể tìm thấy thêm thông tin tại Ghi chú phát hành Foundation cho OS X v10.11 và iOS 9 .

Nếu người quan sát có thể được lưu trữ dưới dạng tham chiếu yếu 0 thì bộ lưu trữ bên dưới sẽ lưu người quan sát dưới dạng tham chiếu yếu 0, hoặc nếu đối tượng không thể được lưu trữ yếu (tức là nó có cơ chế giữ lại / giải phóng tùy chỉnh sẽ ngăn thời gian chạy từ khả năng lưu trữ đối tượng yếu) nó sẽ lưu trữ đối tượng dưới dạng tham chiếu zeroing không yếu. Điều này có nghĩa là người quan sát không bắt buộc phải hủy đăng ký trong phương thức phân bổ của họ.

Những người quan sát dựa trên khối thông qua phương thức - [NSNotificationCenter addObserverForName: object: queue: usingBlock] vẫn cần được hủy đăng ký khi không còn được sử dụng vì hệ thống vẫn giữ một tham chiếu mạnh mẽ đến những người quan sát này.


1
Tôi tò mò, nó có hoạt động tương tự cho các đại biểu không? Tôi đã thấy trong iOS8, các đại biểu chiếm bộ nhớ và không được giữ lại. Tôi đã từng viết delegate = nilbằng dealloc()phương pháp. Nó có hoạt động như nhau từ bây giờ không?
Kampai

1
Theo nguyên tắc chung, các đại biểu nên được khai báo là tham chiếu yếu và không cần làm việc khác.
Nikola Milicevic

Vì bạn đã đề cập cụ thể rằng nó không hoạt động đối với những người quan sát dựa trên khối: Bạn có thể giải thích tại sao không? Có cách nào để giải quyết vấn đề đó không? ví dụ [bản thân yếu đuối]
Philipp Jahoda

62

Bạn có thể sử dụng ba phương pháp:

  1. sau popViewController, trở lại navigationControllerhoặc dismissViewControllerAnimated:

    deinit {
        print("Remove NotificationCenter Deinit")
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }
    
  2. viewDidDisappear, xóa sau khi nó đã là bộ điều khiển chế độ xem tiếp theo:

    override func viewDidDisappear(animated: Bool) {
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }
    
  3. viewWillDisappear - trước khi mở chế độ xem tiếp theo:

    override func viewWillDisappear(animated: Bool) {
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }
    

Cú pháp Swift 3.0:

NotificationCenter.default.removeObserver(self)

1
Tôi đoán là deinit là lựa chọn tốt nhất ở đây.
Glenn Posadas

Kể từ iOS 9, theo @Nikola Milicevic, những người quan sát sẽ tự động bị xóa cho bạn trừ khi bạn đang sử dụng những người quan sát dựa trên khối.
Crashalot

Việc loại bỏ quan sát viên khi bạn rời khỏi bộ điều khiển có làm mất đi mục đích của việc có quan sát viên không? Và deinit chỉ hoạt động khi bạn chuyển từ lớp này sang lớp khác theo lập trình mà không sử dụng bảng phân cảnh?
Cyril

18

Trong Swift 4.2, đây là một trong những cách bạn có thể xóa trình quan sát

deinit {
    NotificationCenter.default.removeObserver(self, name: Notification.Name.Identifier, object: nil)
}

thiết lập thông báo addObserver trong lớp viewDidLoad

override func viewDidLoad() {
    super.viewDidLoad()
    NotificationCenter.default.addObserver(self, selector: #selector(didReceivedItemDetail), name: Notification.Name.Identifier, object: nil)
}

2
Lưu ý rằng trong điều kiện mạng chậm và một số hoạt động nhất định của người dùng, tức là điều hướng đi trong một chế độ xem bận rộn có thể không được gọi. Tôi đã thấy điều này trong các bài kiểm tra.
GordonW,

2
@GordonW nếu phương thức deinit của bạn không được gọi vào cuối vòng đời của bộ điều khiển chế độ xem thì có vấn đề về bộ nhớ trong lớp đó.
Ashim Dahal


4

Tôi cũng muốn chỉ ra rằng bạn nên sử dụng phương pháp này:

func addObserver(_ observer: Any, selector aSelector: Selector, name aName: NSNotification.Name?, object anObject: Any?)

Thay vì

func addObserver(forName name: NSNotification.Name?, object obj: Any?, queue: OperationQueue?, using block: @escaping (Notification) -> Void) -> NSObjectProtocol

Sau này sẽ không loại bỏ người quan sát (Gần đây Ran có vấn đề này). Trước đó sẽ xóa trình quan sát nếu bạn đang sử dụng iOS9.


Khi nào người trước loại bỏ người quan sát?
Shubham

@Shubham Kiểm tra cái này
Guy Daher

Tôi nghĩ rằng đó là bởi vì bạn có chu kỳ giữ lại trong phương pháp thứ hai và đã không loại bỏ trình quan sát theo cách thủ công trong deallocphương pháp.
Nik Kov


0

Cũng tốt nếu bạn thêm người quan sát của mình vào viewWillAppear()và xóa họ trongviewWillDisappear()


0

Swift 5

Tôi có một ứng dụng trò chuyện, vì vậy bất cứ khi nào tôi chuyển từ ChatLogViewController của mình sang một số viewController khác và sau đó quay lại, tôi có thêm 1 Observer thông báo bàn phím của mình. Để loại bỏ điều đó, tôi xóa tất cả những người quan sát khi tôi thay đổi viewController hoặc biến mất khỏi chatLogViewController của mình .

override func viewDidDisappear(_ animated: Bool) {    
    super.viewDidDisappear(animated)

    NotificationCenter.default.removeObserver(self)
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.