Memory Management in Swift - Quản lý bộ nhớ trong Swift (Phần 2)

Ở bài này mình sẽ giải thích weak để làm gì? Lúc nào thì sử dụng weak?

TH 1: Strong - Strong

Các bạn vào bài viêt trước, vào link github tải bài mẫu về. Các bạn thay đổi giống như sau lúc này 2 liên kết sẽ là strong.

Nhìn vào method prepare ta thấy nextVC đang tham chiếu đến màn tiếp theo. Còn nextVC?.previousVC = self chính là màn thiếp theo đang tham chiếu đến màn hiện tại.

Bây giờ ta run app lên

TH1.1

  1. Di chuyển từ VC1 -> VC2 -> VC3

Lưu ý: Khi VC1 next VC2: VC1 đang tham chiếu đến VC2 nextVC = segue.destination as? BaseViewController và VC2 đang tham chiếu đến VC1 nextVC?.previousVC = self, 2 việc này đều diễn ra trong methor prepare tại VC1

  1. Sau đó quay lại VC2 và next sang VC3

Khi đang ở VC2 bạn next sang VC3, một VC3 mới đã được khởi tạo. Lúc này VC2 sẽ next sang VC3 mới. Nếu không tin các bạn thử cho Text Field vào VC3, run app trong VC3 bạn ghi một đoạn text, quay về VC2 rùi next sang VC3 xem có còn không. Điều này mình sẽ giải thích ở bài khác

Màn VC3 cũ đã không còn dùng đến nữa. Không có ai tham chiếu đến nó (nextVC tham chiếu đến VC3cũ không còn tồn tại, nên tham chiếu từ VC3 đến VC2nextVC?.previousVC = self viết tại VC2 không được sử dụng) Reference count = 0. Hàm deinit được chạy vào (print ra VC3 - deinit), ô nhớ được giải phóng. Theo cơ chế ARC.

TH1.2

  • Bây giờ thử lại VC1 -> VC2 -> VC3 -> VC2 -> VC1 -> VC2 -> VC3

VC1 next sang VC2 mới. VC1 không tham chiếu đến VC2 cũ nên tham chiếu từ VC2 cũ về VC1 cũng mất (giải thích ở trên). Lúc này VC2 cũ và VC3 cũ không được sử dụng nữa. Nhưng chúng vẫn ôm nhau sống vì VC2 tham chiếu đến VC3 (nextVC = segue.destination as? BaseViewController) Reference count = 1 và VC3 tham chiếu đến VC2 (nextVC?.previousVC = self) Reference count = 1 việc này đều diễn ra trong methor prepare tại VC2. Theo cơ chế ARC, bộ nhớ không được giải phóng, Đây được gọi là Retain Cycle hay Leaking Momery - rò rỷ bộ nhớ. Nếu bạn cứ next qua lại liên tục, bộ nhớ Ram sẽ liên tục tăng lên dẫn đến đơ app.

TH2: Weak - Strong

TH2.1

  • Di chuyển VC1 -> VC2 -> VC3 -> VC2 -> VC3

Ta thấy ngay khi next từ VC2 sang VC3 mới. Hàm deinit đã được chạy vào. Nghĩa là ô nhớ chứa VC3 đã được giải phóng. Do cơ chế ARC, nó đếm số reference count = 0.

  • Khi khai báo object var nextVC: BaseViewController? mặc định được hiểu là một tham chiếu với liên kết strong (mạnh)
  • Còn việc thêm weak phía trước được hiểu là liên kết weak (yếu), không làm tăng reference count

TH2.2

  • Di chuyển VC1 -> VC2 -> VC3 -> VC2 -> VC1 -> VC2 -> VC3

Khi ta quay về VC1, vẫn có liên kết strong từ VC1 sang VC2 và VC2 sang VC3. Nhưng ngay khi ta next VC1 sang VC2 mới. Liên kết strong từ VC1 sang VC2 cũ đã mất. Liên kết weak từ VC2 cũ sang VC1 cũng mất theo (1). VC2 cũ còn một tham chiếu từ VC3 cũ đến nó, nhưng tham chiếu này là weak => VC2 cũ có Reference count = 0, VC2 được giải phóng => (2)(3) cũng mất theo, VC3 được giải phóng vì không còn liên kết nào.

TH3: Strong - Weak

TH3.1

  • Di chuyển VC1 -> VC2 -> VC3 -> VC2

Khi ta từ VC3 quay về VC2, nhảy vào luôn hàm deinit. Tại sao vậy? Vì liên kết từ VC2 sang VC3 là weak, nên khi quay về VC2, không còn ở VC3, reference count tại VC3 bằng 0, VC3 được giải phóng luôn.


  • Đến đây chắc các bạn đã nắm được quy luật hoạt động. Nên các bạn tự giải thích các trường hợp còn lại.
  • Vậy khi nào thì sử dụng weak? Khi bạn thấy một ô nhớ không còn sử dụng đến nữa, nhưng vẫn chưa được deinit. Khi đó bạn sử dụng đến weak. Lưu ý khi sử dụng weak bắt buộc khai báo var và mặc định là optional. Bởi nó sẽ tự động là nil khi được giải phóng bộ nhớ. Khi các bạn kéo Outlet sẽ thấy điều này.