Memory Management (ARC) trong Swift
1. Giới thiệu
Swift sử dụng Automatic Reference Counting (ARC) để quản lý bộ nhớ tự động.
2. Reference Counting hoạt động
class Person {
let name: String
init(name: String) {
self.name = name
print("\(name) được khởi tạo")
}
deinit {
print("\(name) bị hủy")
}
}
// Reference count = 1
var person1: Person? = Person(name: "John")
// Reference count = 2
var person2 = person1
// Reference count = 1
person1 = nil
// Reference count = 0 → deinit được gọi
person2 = nil
// Output: John bị hủy3. Strong Reference Cycles (Vấn đề)
class Person {
let name: String
var apartment: Apartment?
init(name: String) { self.name = name }
deinit { print("\(name) deinit") }
}
class Apartment {
let number: Int
var tenant: Person? // ⚠️ Strong reference
init(number: Int) { self.number = number }
deinit { print("Apartment \(number) deinit") }
}
var john: Person? = Person(name: "John")
var apt: Apartment? = Apartment(number: 101)
// Tạo cycle
john?.apartment = apt
apt?.tenant = john
// Memory leak! deinit không được gọi
john = nil
apt = nil4. Weak References
class Person {
let name: String
var apartment: Apartment?
init(name: String) { self.name = name }
deinit { print("\(name) deinit") }
}
class Apartment {
let number: Int
weak var tenant: Person? // ✅ Weak reference
init(number: Int) { self.number = number }
deinit { print("Apartment \(number) deinit") }
}
var john: Person? = Person(name: "John")
var apt: Apartment? = Apartment(number: 101)
john?.apartment = apt
apt?.tenant = john
// Bây giờ deinit được gọi đúng
john = nil // "John deinit"
apt = nil // "Apartment 101 deinit"5. Unowned References
class Customer {
let name: String
var card: CreditCard?
init(name: String) { self.name = name }
deinit { print("\(name) deinit") }
}
class CreditCard {
let number: String
unowned let customer: Customer // Luôn có customer
init(number: String, customer: Customer) {
self.number = number
self.customer = customer
}
deinit { print("Card \(number) deinit") }
}
var john: Customer? = Customer(name: "John")
john?.card = CreditCard(number: "1234", customer: john!)
john = nil
// John deinit
// Card 1234 deinit6. Weak vs Unowned
| Weak | Unowned | |
|---|---|---|
| Optional | Có (?) | Không |
| Khi nil | Tự động = nil | Crash nếu truy cập |
| Dùng khi | Reference có thể nil | Reference luôn tồn tại |
// Weak: delegate có thể nil
class TableView {
weak var delegate: TableViewDelegate?
}
// Unowned: child luôn có parent
class Child {
unowned let parent: Parent
}7. Closures và Capture Lists
class ViewController {
var name = "Main"
func setupHandler() {
// ⚠️ Strong capture tạo cycle
let handler = {
print(self.name)
}
// ✅ Weak capture
let safeHandler = { [weak self] in
guard let self = self else { return }
print(self.name)
}
// ✅ Unowned capture
let unownedHandler = { [unowned self] in
print(self.name)
}
}
}8. Capture List với Multiple Values
class DataLoader {
var data: [String] = []
func load() {
let url = URL(string: "https://api.com")!
URLSession.shared.dataTask(with: url) { [weak self, url] data, _, _ in
// url được capture by value
print("Loaded from: \(url)")
// self được capture weakly
self?.data = ["loaded"]
}
}
}9. Common Patterns
// Pattern 1: Completion handlers
class NetworkManager {
func fetchData(completion: @escaping (Data) -> Void) {
// ...
}
}
class ViewModel {
let network = NetworkManager()
var data: Data?
func load() {
network.fetchData { [weak self] data in
self?.data = data
}
}
}
// Pattern 2: Delegate pattern
protocol DownloadDelegate: AnyObject {
func didFinish()
}
class Downloader {
weak var delegate: DownloadDelegate?
}
// Pattern 3: Timer
class TimerController {
var timer: Timer?
func start() {
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] _ in
self?.tick()
}
}
func tick() { print("Tick") }
deinit {
timer?.invalidate()
}
}10. Debug Memory Issues
// Sử dụng deinit để debug
class DebugObject {
let id: String
init(id: String) {
self.id = id
print("🟢 Created: \(id)")
}
deinit {
print("🔴 Destroyed: \(id)")
}
}
// Xcode Instruments: Leaks & Allocations
// Debug Memory Graph trong Xcode📝 Tóm tắt
- ARC tự động đếm references và giải phóng bộ nhớ
- Strong reference cycles gây memory leak
weak- optional, có thể nilunowned- non-optional, crash nếu accessed khi nil- Closures: dùng
[weak self]hoặc[unowned self] - Delegates nên là
weak
Last updated on
Swift