Value type và Reference type – ARC in Swift
1. Value type và Reference type:
Là một lập trình viên iOS chúng ta biết rằng, Apple khuyên chúng ta sử dụng struct và enum nhiều hơn. Lí do là chúng là kiểu Value type, dẫn tới việc bộ nhớ của chúng không ảnh hưởng tới nhau. An toàn hơn khi truy cập và sử dụng
Vậy với Class, Closure có escaping thì sao, chúng là kiểu reference type -> Câu hỏi đặt là ra là sao để quản lý vùng nhớ của chúng, để tránh tạo instance lẫn lộn, gây ra leak memory. Câu trả lời của Apple đó là ARC
Noted:
Class sẽ được khởi tạo trong heap -> nên có thể sử dụng cho data có size lớn
Struct sẽ được khởi trong stack -> nên không thể dùng cho data có size lớn, nó sẽ dẫn tới phình stack nhanh. Vậy nên chúng ta chỉ nên dùng cho những data có size nhỏ.
Còn Heap với Stack là gì, hẹn các bạn ở một bài khác nhé.
2. Vậy ARC là gì?
Automatic Reference Counting: Dịch theo nghĩa đen tiếng Việt là tự động đếm số lượng reference.
Theo Apple định nghĩa về ARC: là cơ chế Swift sử dụng để theo dõi và quản lý bộ nhớ mà ứng dụng sử dụng. Từ đó giúp bạn phát triển ứng dụng mà không cần quan tâm tới việc quản lý bộ nhớ như một số ngôn ngữ khác, hay là Objective – C chưa implement ARC. ARC sẽ tự động frees up( hủy bỏ vùng nhớ của các strong reference = 0)
ARC chỉ áp dụng cho instance của classes. Structures và Enumerations là kiểu value types, không phải kiểu references types và không được lưu trữ dưới dạng tham chiếu.
3. ARC hoạt động như thế nào?
Khi mà chúng ta tạo một instance của một class, ARC sẽ phân bổ một vùng nhớ của bộ nhớ để lưu trữ thông tin của instance đó
Khi mà instance không được sử dụng trong một thời gian đủ dài, ARC sẽ tự động frees up memory được sử dụng bởi instance đó để giải phóng vùng nhớ để sự dụng vào các mục đích khác,
Để xác định một biến không còn được sử dụng, ARC sẽ đếm số lượng strong reference của các object khác đang trỏ vào chính nó. Chỉ khi số lượng strong reference = 0 thì biến đó mới được giải phóng khỏi bộ nhớ.
4. Strong reference
Ở đây chúng ta có pen1, pen2, pen3 đều là kiểu strong reference chỉ về vùng nhớ của class Pen.
Khi ta gán:
pen1 = nil
pen2 = nil
=> ARC sẽ đếm strong reference trong trường họp này sẽ là = 1. vì còn 1 intance được giữ lại bởi pen3. Nên trong trường hơp này ARC không giải phóng vùng nhớ cho Pen.
5. Reference giữa các Class
Khi chúng ta gán person và car = nil thì ta thấy person có car đang chỉ tới vùng nhớ của của Car kiểu strong reference, khi person = nil, thì strong reference này = 1 ( instance chưa bị hủy đi), tương tự với car, tồn tại instance trỏ tới vùng nhớ của Peson chưa được giải phóng:
-> Trong trường hợp này strong reference = 2
-> Tạo retain cycle, dẫn tới leak memory ( Khi bộ nhớ bị đầy dẫn tới app bị chậm, hoặc dừng lại, hoặc thoát ra đột ngột)
Vậy để giải quyết bài toán trên Apple đã đưa ra hai kiểu định nghĩa reference đó là weak và unowned.
6. Weak reference
Khi chúng ta tạo một biến là kiểu weak reference, thì ARC sẽ hiểu reference counting của nó = 0 ( vì nó không phải là strong reference nên không làm tăng retain count. Từ bộ đếm counting không tăng lên không dẫn tới việc leak memory.
Ở đây khi chúng ta set cong = nil, Car sẽ không còn reference nào chỉ đến. Từ đó vùng nhớ được giải phóng, và khi đó strong reference của Person trỏ đến Car cũng bị hủy.
Set honda = nil, ở thời điểm này không còn strong nào trỏ đến -> instance đuợc giải phóng khỏi vùng nhớ. Không làm leak memory
7. Unowned reference
Tương tự như weak thì khi tạo một biến kiểu unowned, Counting Reference không tăng lên. ARC không giữ instance và không gây ra leak memory.
Note: “ Unowned không sử dụng cho kiểu dữ liệu optional, một instance A unowned reference ( trỏ ) đến một instance B mà instance B đó có vòng đời bằng hoặc lớn hơn instance A, nên khi bạn truy cập một biến unowned sau khi nó đã giải phóng khỏi vùng nhớ sẽ dẫn tới crash. Vì vậy khi dùng unowned cần cẩn thận!”
Tương tự như weak khi chúng ta set cong = nil, Car sẽ không còn reference nào chỉ đến. Từ đó vùng nhớ được giải phóng, và khi đó strong reference của Person trỏ đến Car cũng bị hủy.
set honda = nil, ở thời điểm này không còn strong nào trỏ đến -> instance đuợc giải phóng khỏi vùng nhớ. Không làm leak memory
8. Strong Reference Cycles for Closures:
Closure cũng giống như class là reference types.
Trong một class, nếu một property là closure và trong closure đó lại dùng lại property / method của class nữa ( xài self.property ) thì sẽ xảy ra hiện tượng retain cycle như các ví dụ ở trên.
Ta có ví dụ sau:
9. Resolving Strong Reference Cycles for Closures
Để giải quyết vấn đề retain cycle trong closure, swift đã cung cấp cho chúng ta một giải pháp đó là: closure capture list.
Capture list sẽ quy định luật để lấy giá trị của property trong closure. Tức là lấy self.property / self.method như thế nào.
Ta dùng syntax sau ở phần body của closure:
[ weak self ] in
hoặc:
[ unowned self ] in
hoặc lấy nhiều property / method cũng được:
[ weak self, unowned self.property, … ] in
carbon-8
Thanks for reading!