ViewController Lifecycle là trình tự các phương thức mà UIKit tự động gọi khi quản lý màn hình trong iOS. Theo Tài liệu Apple, mỗi UIViewController trải qua một tập hợp các trạng thái có thể dự đoán: từ tạo View đến khi xuất hiện và ẩn đi. Hiểu thứ tự và mục đích của các phương thức này là điều kiện cần thiết cho hoạt động ổn định của ứng dụng iOS.
Các điểm chính
ViewController Lifecycle là tập hợp các phương thức mà UIViewController nhận từ UIKit trong suốt vòng đời của nó. Mỗi màn hình trong ứng dụng iOS tuần tự trải qua các giai đoạn tạo, tải View, xuất hiện trên màn hình, ẩn đi và giải phóng bộ nhớ. UIKit tự động gọi các phương thức tương ứng ở mỗi giai đoạn và nhà phát triển ghi đè chúng để thêm logic riêng.
Kiến trúc UIViewController là nền tảng của UIKit và vẫn còn phù hợp ngay cả trong kỷ nguyên SwiftUI — nhiều dự án vẫn sử dụng cách tiếp cận cổ điển hoặc kiến trúc kết hợp. Hiểu Lifecycle cho phép bạn dự đoán khi nào subviews có sẵn, khi nào an toàn để sửa đổi bố cục và thao tác nào cần thực hiện khi màn hình xuất hiện hoặc ẩn đi.
Mỗi phương thức của vòng đời có một mục đích cụ thể: một số được gọi một lần trong toàn bộ vòng đời của bộ điều khiển, một số khác — mỗi lần xuất hiện hoặc ẩn đi. Trộn lẫn logic giữa các phương thức dẫn đến lỗi khó tìm: rò rỉ bộ nhớ, cập nhật dữ liệu không chính xác và yêu cầu mạng không cần thiết.
Sáu phương thức tạo thành vòng đời hoàn chỉnh của UIViewController. Thứ tự gọi là cố định và không phụ thuộc vào phương thức điều hướng — push, present hay unwind segue đều tuân theo cùng một lịch trình.
loadView là phương thức đầu tiên của chu trình, được gọi khi View của bộ điều khiển chưa tồn tại. Nếu bạn sử dụng Storyboard, UIKit tự động tải View từ tệp xib. Khi tạo giao diện bằng mã, bạn ghi đè phương thức này bằng cách gán View gốc thủ công. Trong hầu hết các dự án, loadView không được động đến — công việc được thực hiện trong viewDidLoad.
Ghi đè loadView chỉ cần thiết trong các trường hợp cụ thể: khi toàn bộ giao diện được tạo bằng mã không có Storyboard, hoặc khi View gốc cần thuộc lớp không chuẩn. Apple khuyên không nên gọi super.loadView khi ghi đè — bạn hoàn toàn chịu trách nhiệm tạo View.
override func loadView() {
view = UIView()
view.backgroundColor = .white
}
viewDidLoad là phương thức được sử dụng nhiều nhất trong chu trình. Nó được gọi một lần sau khi View được tải vào bộ nhớ nhưng chưa được hiển thị trên màn hình. Tại đây, bạn cấu hình subviews, điền dữ liệu vào bảng, đăng ký ô và đăng ký nhận thông báo kéo dài suốt vòng đời của bộ điều khiển.
Một tính năng quan trọng: viewDidLoad không được gọi lại khi màn hình xuất hiện lại. Nếu bạn cần cập nhật dữ liệu mỗi khi màn hình xuất hiện — hãy sử dụng viewWillAppear. Chỉ đặt các thao tác một lần trong viewDidLoad cần thiết cho cấu hình cơ bản.
viewWillAppear được gọi mỗi lần ngay trước khi View hiển thị cho người dùng. Phương thức này nhận tham số animated cho biết việc xuất hiện có được tạo hiệu ứng hay không. Tại đây, bạn cập nhật dữ liệu, tải lại bảng, cấu hình NavigationBar và ẩn hoặc hiện các phần tử tùy theo trạng thái ứng dụng.
Sử dụng viewWillAppear để đồng bộ hóa trạng thái giữa các màn hình: nếu người dùng có thể đã thay đổi dữ liệu trên màn hình trước đó, phương thức này là nơi thích hợp để cập nhật giao diện. Mỗi lần gọi viewWillAppear đều diễn ra trước khi màn hình xuất hiện, ngay cả khi quay lại từ bộ điều khiển con.
viewDidAppear thông báo rằng View đã xuất hiện hoàn toàn trên màn hình và tất cả hiệu ứng chuyển tiếp đã hoàn tất. Tại thời điểm này, màn hình sẵn sàng cho tương tác — người dùng thấy giao diện đầy đủ và có thể tương tác với nó. Phương thức này phù hợp để bắt đầu hiệu ứng nên diễn ra sau khi xuất hiện, khởi động bộ đếm thời gian và theo dõi số lần hiển thị phân tích.
Không giống như viewWillAppear, viewDidAppear đảm bảo màn hình không chỉ hiển thị mà còn được kết xuất đầy đủ. Nếu bạn bắt đầu hiệu ứng trong viewWillAppear, một số khung hình có thể bị bỏ qua vì UIKit chưa hoàn tất chuyển tiếp. Để có hiệu ứng mượt mà, hãy sử dụng viewDidAppear.
viewWillDisappear được gọi trước khi View biến mất khỏi màn hình — khi chuyển sang bộ điều khiển khác, đóng cửa sổ phương thức hoặc tạm dừng ứng dụng. Đây là nơi thích hợp để lưu trạng thái, hủy đăng ký thông báo, dừng các tiến trình đang hoạt động và giải phóng tài nguyên không cần thiết khi màn hình không hiển thị.
Điều quan trọng cần nhớ: viewWillDisappear không đảm bảo rằng View cuối cùng sẽ biến mất — cử chỉ có thể bị hủy. Do đó, hãy lưu dữ liệu quan trọng trong viewDidDisappear, phương thức chỉ được gọi sau khi thực sự biến mất.
viewDidDisappear hoàn thành chu trình xuất hiện và biến mất. Nó được gọi sau khi View đã bị ẩn khỏi màn hình. Trong phương thức này, hiệu ứng cuối cùng được dừng lại, các đối tượng tạm thời bị xóa và việc lưu dữ liệu bắt đầu trong viewWillDisappear được xác nhận.
Phương thức này cũng diễn ra trước deinit của bộ điều khiển — nếu UIViewController của bạn bị hủy, viewDidDisappear sẽ là phương thức Lifecycle cuối cùng trước khi deinit được gọi. Sử dụng nó để dọn dẹp cuối cùng trước khi đối tượng bị hủy.
Trình tự các lần gọi phụ thuộc vào cách màn hình xuất hiện: lần đầu tiên, khi quay lại hoặc khi được trình bày theo phương thức. Hãy xem xét ba kịch bản chính từ góc nhìn của UIKit.
Khi màn hình xuất hiện lần đầu tiên, UIKit trải qua chu trình tạo đầy đủ: loadView được gọi, sau đó viewDidLoad, sau đó hiệu ứng xuất hiện bắt đầu. Trong quá trình hiệu ứng, viewWillAppear được gọi và sau khi hoàn tất — viewDidAppear. Đây là kịch bản duy nhất mà tất cả các phương thức từ loadView đến viewDidAppear kích hoạt tuần tự.
override func viewDidLoad() {
super.viewDidLoad()
print("viewDidLoad — View đã tải vào bộ nhớ")
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print("viewWillAppear — Sắp xuất hiện")
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
print("viewDidAppear — Màn hình hiển thị hoàn toàn")
}
Khi người dùng quay lại màn hình trước đó, UIKit không gọi lại viewDidLoad — View đã được tải vào bộ nhớ. Thay vào đó, chỉ viewWillAppear và viewDidAppear được gọi trên màn hình quay lại, và trên màn hình hiện tại — viewWillDisappear và viewDidDisappear. loadView và viewDidLoad được bỏ qua vì màn hình đã tồn tại trong ngăn xếp điều hướng.
Trình bày phương thức tuân theo cùng quy tắc: bộ điều khiển mới trải qua chu trình đầy đủ ở lần xuất hiện đầu tiên, trong khi bộ điều khiển hiện tại nhận viewWillDisappear và viewDidDisappear. Khi dismiss, thứ tự đảo ngược: bộ điều khiển quay lại nhận viewWillAppear và viewDidAppear một lần nữa, trong khi bộ điều khiển bị loại bỏ nhận các phương thức cuối cùng. Hành vi này là thống nhất cho tất cả các loại chuyển tiếp trong UIKit.
Hãy xem xét bốn kịch bản chính nơi hiểu Lifecycle ảnh hưởng trực tiếp đến chất lượng mã và trải nghiệm người dùng. Cho mỗi kịch bản, chúng tôi cung cấp ví dụ với các khuyến nghị.
viewDidLoad là nơi thiết lập ban đầu không phụ thuộc vào khả năng hiển thị màn hình. Tại đây bạn cấu hình collectionView, đăng ký tệp nib cho các ô, tạo nguồn dữ liệu và bố cục. Nếu bạn đang tải dữ liệu từ mạng, trong viewDidLoad tốt hơn chỉ nên khởi tạo yêu cầu và cập nhật UI trong viewWillAppear khi màn hình sẵn sàng hiển thị.
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(
MyCell.self,
forCellReuseIdentifier: MyCell.identifier
)
viewModel.loadInitialData()
}
Sử dụng viewWillAppear để đồng bộ hóa dữ liệu mỗi khi màn hình xuất hiện. Ví dụ: nếu người dùng có thể đã thay đổi cài đặt trên màn hình trước đó, tại đây bạn cập nhật các giá trị hiển thị, tải lại bảng và điều chỉnh trạng thái NavigationBar. Điều này đảm bảo màn hình luôn hiển thị dữ liệu cập nhật trong mọi kịch bản điều hướng.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
tableView.reloadData()
navigationController?.setNavigationBarHidden(false, animated: animated)
}
viewDidAppear là lý tưởng để bắt đầu hiệu ứng nên diễn ra sau khi người dùng đã nhìn thấy màn hình. Tại đây bạn cũng gửi sự kiện phân tích: xem màn hình, bắt đầu onboarding hoặc phát video. Bắt đầu hiệu ứng trước khi chuyển tiếp hoàn tất dẫn đến giao diện giật lag — UIKit không có đủ thời gian để chuẩn bị số lượng khung hình cần thiết.
Trong viewWillDisappear, bạn lưu bản nháp, dừng bộ đếm thời gian và hủy đăng ký khỏi NotificationCenter. Đây là khoảnh khắc cuối cùng màn hình vẫn hiển thị và có thể truy cập cho các thao tác yêu cầu ngữ cảnh người dùng. Đối với dữ liệu quan trọng, hãy sử dụng thêm viewDidDisappear như một biện pháp bảo vệ chống lại các cử chỉ bị hủy.
Sử dụng sai các phương thức vòng đời là một trong những nguồn lỗi phổ biến nhất trong ứng dụng iOS. Hãy xem xét các lỗi chính mà nhà phát triển mắc phải ở các giai đoạn khác nhau khi làm việc với UIViewController.
Lỗi thứ nhất — tạo subviews trong init hoặc loadView khi sử dụng Storyboard. Nếu bạn đang sử dụng Interface Builder, đừng ghi đè loadView không cần thiết. Tạo View trong loadView khi có storyboard dẫn đến việc bỏ qua tệp xib và màn hình trống.
Lỗi thứ hai — đăng ký nhận thông báo bàn phím trong viewDidLoad mà không hủy đăng ký. Nếu bạn đã đăng ký UIResponder.keyboardWillShowNotification nhưng không hủy đăng ký khi ẩn màn hình, khối sẽ tiếp tục được gọi ngay cả sau khi deinit của bộ điều khiển — đây là rò rỉ bộ nhớ có khả năng làm ứng dụng bị treo.
Lỗi thứ ba — bộ đếm thời gian và yêu cầu mạng bắt đầu trước khi màn hình xuất hiện. Tải hình ảnh hoặc thực hiện hiệu ứng khi View chưa hiển thị là lãng phí tài nguyên. Di chuyển các bản cập nhật trực quan sang viewWillAppear hoặc viewDidAppear.
Lỗi thứ tư — chỉ lưu dữ liệu trong viewWillDisappear. Với cử chỉ pop tương tác, người dùng có thể bắt đầu vuốt và hủy nó — phương thức đã được gọi nhưng màn hình không biến mất. Hãy sao chép việc lưu quan trọng trong viewDidDisappear hoặc trong trình xử lý applicationDidEnterBackground.
Câu hỏi thường gặp
Một lần — sau khi View được tải vào bộ nhớ. Khi màn hình xuất hiện lại, viewDidLoad không được gọi. Nếu cần tạo lại View, bộ điều khiển phải bị hủy và tạo mới.
UIKit yêu cầu gọi super.viewDidLoad để vòng đời hoạt động chính xác. Nếu không, có thể xảy ra vấn đề với cập nhật bố cục và xử lý chuyển tiếp. Luôn gọi super trước tiên bên trong phương thức.
Không được khuyến nghị. Nếu bộ điều khiển được khởi tạo từ Storyboard, UIKit tự động tải View từ xib. Ghi đè loadView hủy quá trình này và storyboard của bạn sẽ bị bỏ qua.
Đăng ký trong viewDidLoad hoặc viewWillAppear và hủy đăng ký trong viewWillDisappear hoặc viewDidDisappear, sử dụng tham chiếu yếu đến self để tránh rò rỉ bộ nhớ với closure.
Force quit giết tiến trình đột ngột — UIKit không có thời gian để gọi các phương thức Lifecycle. Để lưu dữ liệu, hãy sử dụng thông báo UIApplication.willTerminateNotification trong AppDelegate.
Tóm tắt
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.
Đọc thêm