Swift: weak and unowned

by Quang Huy
553 views

   

Đầu mục bài viết

   

1-ARC

Automatic Reference Counting aka ARC, là 1 tính năng của Swift dùng để đếm số lượng strong reference, và cho phép quản lý việc giải phóng memory khi 1 instance không còn được reference(tham chiếu) đến.

Theo như doc của Apple:

Swift uses Automatic Reference Counting (ARC) to track and manage your app’s memory usage. In most cases, this means that memory management “just works” in Swift, and you don’t need to think about memory management yourself. ARC automatically frees up the memory used by class instances when those instances are no longer needed.

Chúng ta cũng cần nhớ rằng trong swift một reference của được định nghĩa mặc định là kiểu strong

Ví dụ : 

class FirstClass: UIViewController {

    let secondClass = SecondClass()

}

class SecondClass {

}

Trong ví dụ này, class FirstViewController của mình đang strong reference đến instance secondClass

   

2-Vấn đề của strong reference cycle

Như mình đã đề cập bên trên, ARC sẽ giúp chúng ta quản lý, phân bổ memory từ những instance không còn được tham chiếu đến.

Tuy nhiên, câu hỏi đặt ra là điều gì sẽ xảy ra khi có 2 object/instance strong reference đến nhau?

Từ strong việt hóa ra là mạnh, bền, … nghe thôi cũng cảm giác khó phá hỏng, phá hủy nó rồi đúng không? :)))

Thì strong reference cũng thế, strong reference trỏ đến 1 instance/object và sở hữu nó, quyết định đến sự tồn tại của instance/object đó. 

ARC không thể có cách nào giải phóng được memory từ những kiểu instace/object này, và điều này sẽ dẫn đến memory leak.

Trong thực tế những project đã làm, thì mình rất hay thường gặp những case strong reference, và mình cũng rất hay vô tình tạo ra strong reference :v 

Case mình hay gặp nhất là case khi khởi tạo delegate:

protocol DemoDelegate {
    func demoFunc()
}

class FirstClass {
    var delegate: DemoDelegate?
}

Trông ví dụ này có vẻ quen đúng không? :))) ở đây mình có 1 protocol DemoDelegate, và khởi tạo delegate này trong FirstClass. Và FirstClass và delegate DemoDelegate đang strong reference đến nhau.

Để giải quyết vấn đề này, chúng ta có weak và unowned.

 

Weak và Unowned

 

  1. Weak

Trái ngược với strong pointer, weak pointer trỏ đến một instance/object và không quyết định đến sự tồn tại của instance/object đó.

Vì thế nên ARC có thể dễ dàng giải phóng bộ nhớ từ instance/object này kể cả chúng ta vẫn còn đang tham chiếu đến nó 

=> Weak reference rất hữu ich, giúp chúng ra tránh được việc vô tình hay cố ý tạo ra 1 strong reference cycle.

Note: Weak reference không thể sử dụng với việc khởi tạo instance/object là let bởi vì instance/object ở một vài case vẫn phải truyền nil.

Việc sử dụng weak reference thực sự quan trọng, ví dụ nếu chúng ta để ý hơn và xem source code bên trong definition của UITableView, ta có thể nhận thấy rằng tất cả delegate(bao gồm 2 var quen thuộc là dataSource và delegate) đều được sử dụng với weak khi khởi tạo.

Và mình nghĩ là đến apple còn để ý và chú trọng đến việc sử dụng weak, tại sao chúng ta lại không làm như thế ? :v

protocol DemoDelegate {
    func demoFunc()
}

class FirstClass {
    weak var delegate: DemoDelegate?
}

Ok, thêm weak là xong, đơn giản phải không nào.

Tuy nhiên, khi thêm weak, lại nảy sinh vấn đề không thể compile đoạn code:

Để giải quyết, theo doc Protocols của apple:

Use a class-only protocol when the behavior defined by that protocol’s requirements assumes or requires that a conforming type has reference semantics rather than value semantics.

Để fix, chỉ cần thêm keyword class là được

protocol DemoDelegate: class {
    func demoFunc()
}

class FirstClass {
    weak var delegate: DemoDelegate?
}

  2. unowned

Một variable kiểu unownevề cơ bản giống với variable kiểu weak, nhưng khác nhau ở chỗ: compiler sẽ make sure rằng variable này khi được gọi đến sẽ không có giá trị nil.

Vậy thực sự trong trường hợp nào nên sử dụng unowned thay vì sử dụng weak ? Vẫn theo doc của Apple: 

Like a weak reference, an unowned reference doesn’t keep a strong hold on the instance it refers to. Unlike a weak reference, however, an unowned reference is used when the other instance has the same lifetime or a longer lifetime.

Để hiểu rõ hơn, mình có 2 ví dụ như này

Ví dụ của weak reference: mình sở hữu 1 chiếc xe đạp, nhưng 1 hôm đẹp trời nào đó, mình đổi ý không muốn đạp xe nữa, thì ở đây chiếc xe đạp vẫn còn đó, có chăng chỉ là đổi chủ, trong khi mình đang ngồi 1 chiếc 4 bánh nào đó :v 

Ví dụ của unowned reference: mình chơi 1 yasuo chẳng hạn :))), thằng nhân vật của mình tăng skill theo cấp độ. Tuy nhiên, khi xám màn hình, những skill đó cũng sẽ xám theo. Có nghĩa rằng là những skill này có tuổi thọ = tuổi thọ của nhân vật mình đang chơi.

Triển khai trong code:

class Yasuo {

    let name: String
    let skill: Skill?

    init(name: String) {
        self.name = name
    }

}

class Skill {

    let damage: Int
    unowned let champ: Yasuo

    init(damage: Int, champ: Yasuo) {
        self.damage= power
        self.champ = champ
    }

}

   

3-Debugging

Vậy là chúng ta đã hiểu phần nào về 2 keyword weak và unowned. Chúng dùng để tránh tình trạng vô tình tạo ra memory leak. 

Nhưng sao để biết được có thực sự tránh được hay không?

Cách đơn giản và xưa như quả đất, là sử dụng print trong 2 method init và deinit

class FirstClass {

    init() {
        print("FirstClass is being initialized")
    }

    deinit { 
        print("FirstClass is being deinitialized") 
    }

}

Và để ý memory trong XCode nữa :v

   

4-Tổng kết

Strong, weak, unowned là những thuật ngữ cơ bản trong swift, tuy nhiên chúng ta thường – do vô ý hoặc chủ quan – bỏ qua chúng.

Điều này không thực sự ảnh hưởng quá lớn với những project nhỏ. 

Tuy nhiên với những app lớn, việc quản lý bộ nhớ ra sao cho hiệu quả là 1 việc quan trọng và đáng lưu ý, bởi vì khi memory leak trở thành 1 vấn đề nghiêm trọng, thì rất khó và tốn effort để fix. 😀

HAPPY CODING!

REFERENCE

https://docs.swift.org/swift-book/LanguageGuide/Protocols.html

https://docs.swift.org/swift-book/LanguageGuide/AutomaticReferenceCounting.html

Leave a Comment

* By using this form you agree with the storage and handling of your data by this website.