ARC và Kỹ thuật quản lý vùng nhớ trong Swift
Mừng xuân Quý Mão 2023!
Đầu năm mới, chúc toàn thể anh chị em Viblo một năm tràn đầy năng lượng,tràn đầy niềm vui và hạnh phúc: Vui trong sức khoẻ, trẻ trong tâm hồn, khôn trong lý tưởng và trưởng thành mọi lĩnh vực.
Việc quản lí bộ nhớ trong mỗi ứng dụng là rất quan trọng. Nếu ứng dụng của bạn sử dụng dữ liệu bừa bãi 1 cách không cần thiết thì sẽ làm memory của ứng dụng tăng cao, và trong trường hợp xấu có thể dẫn đến crash. Swift sử dụng ARC để hỗ trợ developer quản lí ứng dụng của app. Ở bài viết này mình sẽ nói về ARC trong swift.
1. ARC là gì
- ARC là viết tắt của Automatic Reference Counting. Swift sử dụng ARC để giúp bạn việc quản lý bộ nhớ của app.
- ARC sẽ tự động giải phóng bộ nhớ được sử dụng bởi 1 biến khi biến đó không còn được sử dung nữa.
- Tuy nhiên, để ARC có thể biết được khi nào biến đó không còn dc sử dụng, thì ARC phải biết về các relations giữa biến đó vs các biến khác trong code của bạn.
2. Cách ARC hoạt động
- Mỗi khi bạn tạo 1 biến, ARC sẽ tự chia sẻ 1 phần bộ nhớ để lưu trữ biến đó. Khi biến đó không còn được sử dụng, ARC giải phóng biến, lấy lại bộ nhớ để dùng cho những mục đích khác
- Để xác định 1 biến không còn được sử dụng, ARC sẽ đếm số lượng strong reference hiện tại của biến đó đối với các biến khác. Chỉ khi số lượng strong reference = 0 thì biến đó mới được giải phóng bộ nhớ.
Vậy strong reference là gì?
3. Strong reference
Mỗi khi bạn gán 1 biến kiểu class cho 1 property, constant hoặc variable, thì những thứ đó tạo 1 strong reference đến biến đó.
class Car {
var price: Int
init(price: Int) {
self.price = price
}
deinit {
print("Car is being deinitialized")
}
}
var hondaCar: Car? = Car(price: 10000)
Ở đây, bạn gán biến Car(price: 10000) cho varialbe hondaCar
-> hondaCar đã tạo 1 strong reference đến Car(price: 10000)
Thêm dòng code sau và run thử trên playground:
hondaCar = nil
Kết quả thu được ở màn hình console như sau:
Car is being deinitialized
Nếu bạn set honedaCar = nil -> Khi đó Car(price:10000) sẽ không còn strong reference nào trỏ đến -> ARC sẽ giải phóng bộ nhớ cho Car. Tiếp tục thử chạy đoạn code sau và quan sát màn hình console:
class Car {
var price: Int
init(price: Int) {
self.price = price
}
deinit {
print(" Car is being deinitialzed")
}
}
var car1: Car? = Car(price: 10000)
var car2 = car1
var car3 = car1
car1 = nil
car2 = nil
Trong ví dụ trên, sẽ tạo ra 1 sơ đồ relation giữa các instance như sau:
Khi đó nếu chỉ set car1, car2 = nil, thì Car vẫn còn 1 strong reference từ car3 trỏ đến -> ARC sẽ không giải phóng bộ nhớ cho Car trong trường hợp này.
3.1. Strong reference giữa các class
class Person {
var department: Department?
deinit {
print("Person is being deinitialized")
}
}
class Department {
var owner: Person?
deinit {
print("Department is being deinitialized")
}
}
var duy: Person? = Person()
var hadong: Department? = Department()
duy!.department = hadong
hadong!.owner = duy
duy = nil
hadong = nil
Trong ví dụ trên, sẽ tạo ra 1 sơ đồ relation giữa các instance như sau:
- Việc set property department và owner của 2 instance tuan và gotham đã tạo ra 2 strong reference trỏ đến nhau.
- Khi đó Person và Departmen, mỗi thứ sẽ có 2 strong refernce trỏ đến. Vì vậy, khi bạn chỉ set tuan = nil và gotham = nil thì Person và Departmen vẫn sẽ không được xóa khỏi bộ nhớ dù không cần dùng đến nữa.
-> Trường hợp này gọi là retain cycle -> Tạo ra rò rỉ bộ nhớ (memory leaks).
3.2. Xử lí strong reference
Swift cung cấp 2 cách để xử lí strong reference cycle khi bạn làm việc với các biến kiểu class:
- weak reference
- unowned reference
4. Weak reference
- weak refernce: thay vì tạo 1 strong reference, nó sẽ chỉ tạo 1 weak -> ARC sẽ không làm tăng số lượng strong reference trỏ tới biến đó.
- Sử dụng weak reference khi biến còn lại có thời gian tồn tại ngắn hơn(thường bị giải phóng bộ nhớ trước)
class Person {
weak var department: Department?
deinit {
print("Person is being deinitialized")
}
}
Sửa lại ví dụ trên bằng cách sửa department thành kiểu weak( vì department có thời gian tồn tại ngắn hơn người).
Khi đó, sơ đồ relation ship sẽ thành:
- set hadong = nil -> Departmen k còn strong reference nào trò đến -> được giải phóng khỏi bộ nhớ, khi đó strong reference của Departmen trò đến Person cũng sẽ bị mất.
- set duy = nil -> Person không còn strong refernce nào trỏ đến -> ARC giải phóng khỏi bộ nhớ. Kết quả thu được:
Department is being deinitialized
Person is being deinitialized
5. Unowned reference
- Tương tự như weak refernce, nó sẽ không tạo ra 1 strong reference.
- Khác weak reference ở chỗ, unowned dùng cho những biến có thời gian tồn tại lâu hơn. Note: nếu bạn truy cập đến 1 biến unowned sau khi nó đã được giải phóng khỏi bộ nhớ -> app sẽ crash. Vì vậy hãy cẩn thận khi dùng unowned. Sửa ví dụ trên thành như sau và sẽ vẫn thu được kết quả tương tự so với khi dùng weak:
class Department {
unowned var owner: Person?
deinit {
print("Department is being deinitialized")
}
}
Tài liệu tham khảo :
https://docs.swift.org/swift-book/LanguageGuide/AutomaticReferenceCounting.html
Hi vọng sau bài viết này các bạn đọc có thể hiểu một cách khái quát về cơ chế ARC. Cảm ơn các bạn đã đọc và mọi câu hỏi hay ý kiến các bạn hãy để dưới bình luận nhé !!!!
All rights reserved