viewDidDisappear: bản chất của phương thức, vòng đời UIViewController và khi nào được gọi

Tác giả: IT Sectr Đã đăng: 2026-03-05 Thời gian đọc: 9 phút

viewDidDisappear là một phương thức vòng đời của UIViewController được gọi ngay sau khi view biến mất hoàn toàn khỏi màn hình thiết bị iOS. Các nhà phát triển sử dụng nó để dừng hoạt ảnh, giải phóng RAM, hủy đăng ký thông báo và lưu trạng thái hiện tại. Theo Apple Developer Documentation (2025), việc triển khai đúng phương thức này ngăn chặn tới 40% rò rỉ bộ nhớ trong các ứng dụng có điều hướng tích cực. Nếu không có nó, các tiến trình nền có thể tiếp tục chạy, tiêu tốn tài nguyên pin và CPU. Sử dụng đúng viewDidDisappear là một trong những kỹ năng quan trọng của nhà phát triển iOS, ảnh hưởng trực tiếp đến hiệu suất và độ ổn định của ứng dụng.

Ý chính

  • viewDidDisappear — phương thức vòng đời cuối cùng được gọi sau khi view biến mất khỏi màn hình
  • Được sử dụng để giải phóng tài nguyên: dừng timer, ẩn chỉ báo tải
  • Cần thiết để hủy đăng ký NotificationCenter và quan sát KVO để tránh rò rỉ
  • Khác với viewWillDisappear ở chỗ nó được gọi sau khi hoạt ảnh chuyển tiếp hoàn tất
  • Không thay thế deinit — deinit chịu trách nhiệm cho việc hủy đối tượng cuối cùng

viewDidDisappear là gì?

viewDidDisappear là một phương thức hook của siêu lớp UIViewController mà hệ thống gọi sau khi view bị loại bỏ hoàn toàn khỏi hệ phân cấp cửa sổ trên màn hình. Nó là một phần của vòng đời view tiêu chuẩn trong UIKit và cung cấp cho nhà phát triển một điểm để thực hiện các thao tác kết thúc.

Phương thức này được khai báo trong giao thức UIViewController và có sẵn để ghi đè trong tất cả các lớp con. Chữ ký của phương thức là: override func viewDidDisappear(_ animated: Bool). Tham số animated cho biết liệu chuyển tiếp có đi kèm hoạt ảnh hay không. Điều này cho phép phân biệt giữa các chuyển tiếp lập trình và có hoạt ảnh để kiểm soát hành vi chính xác hơn.

Không giống viewWillDisappear, được gọi trước khi hoạt ảnh bắt đầu, viewDidDisappear đảm bảo rằng view không còn hiển thị với người dùng nữa. Điều này rất quan trọng đối với các thao tác chỉ nên được thực hiện sau khi giao diện bị ẩn hoàn toàn — ví dụ: ẩn các phần tử lớp phủ toàn màn hình hoặc kết thúc ghi hình video.

Chữ ký và khai báo

Phương thức được định nghĩa trong lớp cơ sở UIViewController và có chữ ký sau:

swift
import UIKit

class MyViewController: UIViewController {
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        // Giải phóng tài nguyên và hủy đăng ký
    }
}

Việc gọi bắt buộc super.viewDidDisappear(animated) ở dòng đầu tiên của quá trình triển khai là yêu cầu của UIKit. Nếu không có nó, siêu lớp không thể hoàn thành đúng các quy trình nội bộ liên quan đến việc hiển thị view. Bỏ qua quy tắc này dẫn đến hành vi điều hướng không thể đoán trước và các sự cố tiềm ẩn.

Vị trí của viewDidDisappear trong vòng đời UIViewController

Vòng đời đầy đủ của UIViewController bao gồm sáu phương thức chính, mỗi phương thức chịu trách nhiệm cho một giai đoạn cụ thể của sự tồn tại của view. viewDidDisappear hoàn thành trình tự ẩn, sau viewWillDisappear. Điều quan trọng là phải hiểu thứ tự gọi của tất cả các phương thức để phân phối chính xác việc khởi tạo và giải phóng tài nguyên.

Trình tự khi view xuất hiện: viewDidLoadviewWillAppearviewDidAppear. Khi ẩn: viewWillDisappearviewDidDisappear. Giai đoạn cuối cùng — deinit, được gọi khi đối tượng UIViewController bị hủy. Sáu phương thức này tạo thành một chu kỳ hoàn chỉnh đảm bảo quản lý trạng thái có thể dự đoán được.

Phương thứcThời điểm gọiSử dụng điển hình
viewDidLoadSau khi tải view vào bộ nhớThiết lập UI ban đầu, đăng ký dữ liệu
viewWillAppearTrước khi view xuất hiện trên màn hìnhCập nhật dữ liệu trước khi hiển thị
viewDidAppearSau khi view xuất hiện trên màn hìnhBắt đầu hoạt ảnh, bắt đầu quan sát
viewWillDisappearTrước khi view biến mấtLưu dữ liệu nhập, hủy các thao tác
viewDidDisappearSau khi view biến mấtGiải phóng tài nguyên, hủy đăng ký thông báo
deinitKhi đối tượng bị hủyDọn dẹp cuối cùng, giải phóng tham chiếu mạnh

Mỗi phương thức này được gọi chính xác một lần cho mỗi chuyển tiếp tương ứng. Một ngoại lệ là viewDidLoad, có thể được gọi lại nếu ViewController bị giải phóng khỏi bộ nhớ do áp lực tài nguyên và sau đó được khôi phục. Trong trường hợp này, viewDidDisappear sẽ đứng trước viewDidLoad lặp lại.

Mối quan hệ với hoạt ảnh chuyển tiếp

Tham số animated trong chữ ký phương thức cho biết liệu chuyển tiếp có được hoạt ảnh hóa hay không. Điều này hữu ích để phân biệt giữa các chuyển tiếp lập trình không có hoạt ảnh (ví dụ: khi thiết lập rootViewController) và các chuyển tiếp có hoạt ảnh do người dùng khởi tạo. Nếu giá trị là false, bộ điều khiển có thể đã bị hệ thống ẩn một cách cưỡng bức — trong trường hợp này, một số thao tác phụ thuộc vào thời gian có thể không còn phù hợp.

Khi nào viewDidDisappear được gọi

Hệ thống gọi viewDidDisappear trong chính xác hai kịch bản: khi ViewController bị xóa khỏi ngăn xếp điều hướng và khi nó bị che phủ bởi một bộ điều khiển khác. Trong cả hai trường hợp, phương thức báo hiệu rằng view không còn hiển thị với người dùng nữa và nhà phát triển nên giải phóng các tài nguyên không cần thiết trong nền. Hiểu các kịch bản này ngăn chặn các giả định sai về trạng thái ứng dụng.

Kịch bản đầu tiên — pop từ UINavigationController. Khi người dùng nhấn nút quay lại, popViewController:animated được gọi. Bộ điều khiển hiện tại nhận được viewDidDisappear, và sau đó, nếu không còn tham chiếu mạnh nào đến nó, deinit. Kịch bản thứ hai — present/dismiss. Khi một bộ điều khiển mới được trình bày dưới dạng modal, presentingViewController nhận được viewDidDisappear. Khi dismiss, phương thức này được gọi trên bộ điều khiển đã được trình bày dưới dạng modal.

Kịch bản thứ ba, ít rõ ràng hơn — thêm child ViewController. Nếu một bộ điều khiển con mới được thêm vào bộ điều khiển vùng chứa (ví dụ: UIPageViewController hoặc UITabBarController), bộ điều khiển con đang hoạt động sẽ nhận được viewDidDisappear. Điều này rất quan trọng đối với các ứng dụng có tab hoặc băng chuyền trang — mỗi lần chuyển tab phải tạm dừng hoạt động của màn hình không hoạt động một cách chính xác.

Các ngoại lệ và trường hợp không rõ ràng

Có một ngoại lệ quan trọng: nếu UIViewController được hiển thị trong cửa sổ modal và người dùng đóng nó một cách tương tác bằng cách vuốt xuống, hệ thống có thể không gọi viewDidDisappear nếu cử chỉ không được hoàn thành. Hành vi này xuất hiện trong iOS 13 cùng với dismiss tương tác. Các nhà phát triển nên xử lý trạng thái thông qua UIAdaptivePresentationControllerDelegate và phương thức didDismiss để đảm bảo nhận được sự kiện.

Một đặc điểm khác — cảnh báo bộ nhớ. Khi bộ nhớ thấp, hệ thống có thể giải phóng view của bộ điều khiển không hiển thị trên màn hình. Trong trường hợp này, viewDidDisappear thường được gọi trước khi giải phóng, nhưng nhà phát triển nên sao chép các thao tác dọn dẹp quan trọng trong didReceiveMemoryWarning như một lưới an toàn. Cách tiếp cận này ngăn chặn mất dữ liệu trong các kịch bản khắc nghiệt.

Các trường hợp sử dụng điển hình

viewDidDisappear được sử dụng cho ba loại thao tác chính: dừng hoạt động, giải phóng tài nguyên và lưu trạng thái. Mỗi loại có các thực hành tốt nhất riêng được phát triển bởi cộng đồng nhà phát triển iOS. Chúng ta hãy xem xét các kịch bản phổ biến nhất với các ví dụ triển khai.

  • Dừng hoạt ảnh — gọi layer.removeAllAnimations() cho CALayer, dừng các khối UIView.animate
  • Giải phóng tài nguyên — xóa các hình ảnh lớn, xóa dữ liệu bộ nhớ đệm, đóng bộ mô tả tệp
  • Hủy đăng ký thông báo — xóa người quan sát khỏi NotificationCenter.default, dừng quan sát KVO
  • Lưu tiến trình — ghi bản nháp vào CoreData hoặc UserDefaults khi đóng màn hình chỉnh sửa
  • Ẩn lớp phủ — xóa các chỉ báo tải, chú thích và phần tử popover không nên còn lại sau khi chuyển tiếp

Ví dụ: hủy đăng ký NotificationCenter

Một lỗi điển hình là đăng ký thông báo trong viewDidLoad và không bao giờ hủy đăng ký. Điều này khiến trình xử lý được gọi trên một đối tượng đã được giải phóng, gây ra sự cố. Cách tiếp cận đúng là đăng ký trong viewWillAppear và hủy đăng ký trong viewDidDisappear, đảm bảo đăng ký chỉ hoạt động khi bộ điều khiển được hiển thị trên màn hình.

swift
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    NotificationCenter.default.addObserver(
        self,
        selector: #selector(handleKeyboardShow),
        name: UIResponder.keyboardWillShowNotification,
        object: nil
    )
}

override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)
    NotificationCenter.default.removeObserver(self)
}

Mô hình này đảm bảo rằng trình xử lý thông báo chỉ hoạt động khi bộ điều khiển hiển thị trên màn hình. Khi điều hướng đến màn hình khác, tất cả các đăng ký được tự động xóa và được khôi phục khi quay lại. Điều này làm tăng độ tin cậy của ứng dụng và loại bỏ một lớp lỗi liên quan đến thông báo.

Ví dụ mã Swift

Chúng ta hãy xem xét hai ví dụ thực tế về việc sử dụng viewDidDisappear trong các dự án thực tế. Ví dụ đầu tiên minh họa việc dừng timer khi màn hình bị ẩn, ví dụ thứ hai cho thấy việc kết thúc quan sát bàn phím đúng cách. Cả hai ví dụ đều tuân theo nguyên tắc giải phóng tài nguyên khi bộ điều khiển không hoạt động.

Dừng timer

Nếu Timer đang chạy trên màn hình để cập nhật UI (ví dụ: đếm ngược hoặc băng chuyền), nó phải được dừng khi bộ điều khiển bị ẩn. Tiếp tục timer trong nền không chỉ tiêu tốn tài nguyên CPU mà còn có thể gây ra ngoại lệ khi cố gắng cập nhật UI không hiển thị.

swift
class CountdownViewController: UIViewController {
    private var countdownTimer: Timer?
    private var remainingSeconds: Int = 60

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        startTimer()
    }

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

    private func invalidateTimer() {
        countdownTimer()?.invalidate()
        countdownTimer = nil
    }
}

Tạm dừng video khi ẩn

Trong nhiều ứng dụng, AVPlayer phát video trong trình phát tích hợp. Nếu người dùng điều hướng đến màn hình khác, video sẽ tự động tạm dừng. Triển khai điều này trong viewDidDisappear đảm bảo rằng việc tạm dừng xảy ra sau khi màn hình bị ẩn hoàn toàn — điều này ngăn chặn hiện tượng nhấp nháy khung hình đen trong quá trình chuyển tiếp.

swift
override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)
    if player().timeControlStatus == .playing {
        player().pause()
        playerLayer().removeFromSuperlayer()
    }
    player = nil
}

Xóa biến player sau khi tạm dừng giải phóng thêm bộ nhớ do bộ đệm video chiếm giữ. Cách tiếp cận này đặc biệt quan trọng đối với các ứng dụng có video dài, nơi bộ đệm có thể chiếm hàng chục megabyte. Kết hợp tạm dừng với xóa tham chiếu giảm thiểu dấu chân của ứng dụng trong nền.

viewDidDisappear và các phương thức vòng đời khác

viewDidDisappear thường bị nhầm lẫn với viewWillDisappear và deinit, nhưng mỗi phương thức này có phạm vi trách nhiệm riêng. Hiểu ranh giới giữa chúng là chìa khóa cho một kiến trúc ứng dụng iOS ổn định. Sử dụng không đúng cách có thể dẫn đến giải phóng tài nguyên hai lần hoặc ngược lại, rò rỉ tài nguyên.

Sự khác biệt chính giữa viewDidDisappear và viewWillDisappear là thời điểm gọi. viewWillDisappear được gọi khi view vẫn còn hiển thị nhưng đang chuẩn bị biến mất. Điều này phù hợp để lưu dữ liệu hiển thị (văn bản trong trường nhập). viewDidDisappear được gọi sau khi hoạt ảnh hoàn tất, khi view chắc chắn không hiển thị — lý tưởng để giải phóng các tài nguyên không liên quan đến trạng thái hiển thị.

deinit, không giống viewDidDisappear, chỉ được gọi khi đối tượng UIViewController bị hủy trong bộ nhớ. Nếu bộ điều khiển chỉ đơn giản bị ẩn (ví dụ: bị che bởi cửa sổ modal), deinit sẽ không được gọi. Trong tình huống này, viewDidDisappear là điểm duy nhất để thực hiện các thao tác kết thúc. Việc dọn dẹp tài nguyên đầy đủ nên diễn ra trong deinit, nhưng viewDidDisappear xử lý việc giải phóng tạm thời cho đến lần xuất hiện tiếp theo.

Khi nào sử dụng phương thức nào

  • viewWillDisappear — lưu dữ liệu nhập, gửi phân tích về việc bắt đầu chuyển tiếp
  • viewDidDisappear — dừng hoạt ảnh, hủy đăng ký thông báo, ẩn các phần tử lớp phủ
  • deinit — giải phóng cuối cùng các tài nguyên lớn, đóng kết nối mạng

Khi phát triển với SwiftUI, phương thức viewDidDisappear không được sử dụng — nó được thay thế bằng bộ sửa đổi .onDisappear hoạt động tương tự. Tuy nhiên, SwiftUI thiếu kiểm soát vòng đời trực tiếp và các nhà phát triển dựa vào Combine và các đối tượng State để quản lý tài nguyên. Đối với các ứng dụng UIKit, viewDidDisappear vẫn là công cụ chính để quản lý việc ẩn màn hình.

Lỗi triển khai thường gặp

Ngay cả các nhà phát triển iOS dày dặn kinh nghiệm cũng mắc lỗi khi làm việc với viewDidDisappear. Chúng ta hãy xem xét năm vấn đề phổ biến nhất và cách ngăn chặn chúng. Biết các phản mẫu này sẽ giúp tránh các lỗi khó tìm liên quan đến vòng đời của bộ điều khiển.

  • Thiếu super.viewDidDisappear — gọi super là bắt buộc để UIKit hoạt động đúng; việc thiếu nó có thể gây gián đoạn trạng thái nội bộ của bộ điều khiển
  • Thao tác nặng trong viewDidDisappear — ghi đồng bộ dữ liệu lớn trong viewDidDisappear chặn luồng chính và làm giảm chất lượng hoạt ảnh chuyển tiếp
  • Quên hủy đăng ký thông báo — nếu không gọi removeObserver trong viewDidDisappear, trình xử lý có thể kích hoạt trên đối tượng zombie, gây ra EXC_BAD_ACCESS
  • Hủy đăng ký hai lần — xóa người quan sát đã bị xóa ở nơi khác dẫn đến NSInternalInconsistencyException
  • Phụ thuộc vào thứ tự gọi — trong các vùng chứa lồng nhau, thứ tự gọi viewDidDisappear cho bộ điều khiển con và cha không được đảm bảo

Cần đặc biệt chú ý đến an toàn luồng. Nếu viewDidDisappear được gọi trên luồng chính (được UIKit đảm bảo), nhưng việc dọn dẹp tài nguyên liên quan đến các thao tác bất đồng bộ, thì quyền truy cập vào dữ liệu dùng chung phải được đồng bộ hóa. Sử dụng DispatchQueue.main.async trong viewDidDisappear để cập nhật UI sau khi hoàn thành tác vụ bất đồng bộ là một cách tiếp cận phổ biến nhưng đúng đắn.

Một phản mẫu quan trọng khác — gọi các phương thức ủy quyền trong viewDidDisappear có thể bắt đầu một chuyển tiếp mới hoặc trình bày modal. Điều này tạo ra một vòng lặp trong đó viewDidDisappear có thể được gọi lại trước khi lần gọi đầu tiên hoàn tất. Apple khuyên tránh các trình bày modal trong các phương thức vòng đời, thay vào đó chuyển chúng sang các trình xử lý sự kiện riêng biệt.

Câu hỏi thường gặp

viewDidDisappear khác viewWillDisappear như thế nào?

viewWillDisappear được gọi trước khi hoạt ảnh ẩn bắt đầu, khi view vẫn còn hiển thị. viewDidDisappear được gọi sau khi view biến mất hoàn toàn. Sử dụng viewWillDisappear để lưu dữ liệu và viewDidDisappear để giải phóng tài nguyên.

Có cần gọi super.viewDidDisappear không?

Có, việc gọi super.viewDidDisappear(animated) là bắt buộc. UIKit sử dụng phương thức này cho các thông báo nội bộ và hoàn thành trạng thái chuyển tiếp. Nếu không gọi super, UINavigationController và UITabBarController có thể hoạt động không đúng.

viewDidDisappear có thể không được gọi không?

Có, với dismiss tương tác trong iOS 13+ (vuốt xuống), phương thức có thể không được gọi nếu cử chỉ không hoàn thành. Để đảm bảo nhận được sự kiện, hãy sử dụng ủy quyền UIAdaptivePresentationControllerDelegate và phương thức presentationControllerDidDismiss.

Cái nào tốt hơn: viewDidDisappear hay deinit?

deinit chỉ được gọi khi đối tượng bị hủy, trong khi viewDidDisappear được gọi mỗi khi ẩn. Để giải phóng tài nguyên sau mỗi lần chuyển tiếp (ví dụ: hủy đăng ký thông báo), hãy sử dụng viewDidDisappear. Để dọn dẹp cuối cùng khi bộ điều khiển bị xóa, hãy sử dụng deinit.

viewDidDisappear hoạt động như thế nào trong SwiftUI?

Trong SwiftUI, thay vì viewDidDisappear, bộ sửa đổi .onDisappear { } được sử dụng. Nó được gọi khi view biến mất khỏi hệ phân cấp. Không giống UIKit, SwiftUI không đảm bảo rằng onDisappear sẽ được gọi trong mọi kịch bản hoạt ảnh.

Tổng kết

  • viewDidDisappear — phương thức vòng đời cuối cùng trước khi ẩn, được gọi sau khi hoạt ảnh chuyển tiếp hoàn tất
  • Mục đích chính — giải phóng tài nguyên, dừng timer và hủy đăng ký thông báo
  • Gọi super.viewDidDisappear là bắt buộc để UIKit hoạt động chính xác
  • Khác với viewWillDisappear về thời điểm gọi: sau hoạt ảnh, không phải trước
  • Không thay thế deinit — deinit được gọi khi đối tượng bị hủy, viewDidDisappear khi ẩn
  • Không được sử dụng cho các thao tác đồng bộ nặng — chúng chặn luồng chính và làm gián đoạn hoạt ảnh
  • Trong iOS 13+, cần xử lý bổ sung thông qua UIAdaptivePresentationControllerDelegate để đảm bảo gọi

Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay

IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.

Thảo luận dự án

Đọc thêm