Author: congphanquang1

  • Value type và Reference type – ARC in Swift

    Value type và Reference type – ARC in Swift

    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!

  • Bắt đầu với RxSwift ( Phần 1)

    Bắt đầu với RxSwift ( Phần 1)

    I. Giới thiệu

    Một trong những điều quan trọng của lập trình hướng đối tượng (OOP) và hướng thủ tục đó là imperative (lập trình mệnh lệnh). Chúng ta cần sử dụng những câu lệnh để thay đổi trạng thái của chương trình.

    Vậy câu hỏi đặt ra làm sao để trạng thái của chương trình có thể thay đổi một cách tự động, liệu ngôn ngữ, khái niệm lập trình nào làm được việc đó không ?

    → Câu trả lời đó là reactive programming.

    Khi mà ứng dụng của bạn phản ứng lại với những thay đổi của data, reactive programming sẽ giúp chúng ta thực hiện điều đó. Nó giúp chúng ta tâp trung vào việc xử lí logic và không cần quan tâm tới việc thay đổi giữa các trạng thái (state) với nhau.

    Chúng ta có thấy trong swift sử dụng KVO và didSet để thiết lập cho lập trình phản ứng (reactive), nhưng việc thiết lập cho dữ liệu lớn, hoặc bài toán phức tạp hơn khá rắc rối. Vậy nên chung ta sẽ dùng tới thư viện thứ 3 đó là RxSwift

    Note*: “KVO là một khái niệm chúng ta sẽ gặp nhiều khi sử dụng SwiftUI, các bạn có thể search thêm về khái niệm này nhé.”

    Thư viện RxSwift sẽ giúp chúng ta giải quyết vấn đề trên qua lập trình bất đồng bộ (asynchronous programming).

    Để không tốn thời gian của các bạn mình sẽ đi nhanh qua các thành phần mà RxSwift cung cấp

    II. Các thành thần (components) chính của RxSwift:

    • Observable và Observer
    • Subject
    • DisposeBag
    • Operators
    • Schedules

    Observable và Observer

    Có rất nhiều thuật ngữ để mô tả cho lập trình bất đồng bộ, ở đâu mình sẽ sử dụng thuật ngữ observebal và Observer luôn nhé.

    • Observer lắng nghe Observable.
    • Observable phát ra các items hoặc gửi các notifications đến các Observer bằng cách gọi các Observer methods.
    Khái niệm Observable đến từ observer design pattern là một đối tượng thông báo cho các đối tượng theo dõi về một điều gì đó đang diễn ra

    • Một Observer đăng ký lắng nghe một Observable, sau đó nó sẽ xử lý một item hoặc nhiều các item mà Observable phát ra.

    Chúng ta có thể đăng kí tới một Observable sequence thông qua subscribe(on:(Event<T>)->()).

    Trong RxSwift một sự kiện sẽ chỉ là một trong Enumeration Type với 3 trạng thái có thể xảy ra:

    .onNext(value: T) -> Observable  gọi hàm onNext  có tham số là item, item này là một trong các tập items của Observable

    .onError(error: Error) -> Được gọi khi Observable  kết thúc với một lỗi xảy ra trong quá trình chuyển đổi, xử lý dữ liệu.

    .onCompleted -> Observable  gọi hàm này sau khi hàm onNext  cuối cùng được gọi, nếu không có bất kì lỗi nào xảy ra.

    Ví dụ với code Swift: 

    let obj = Observable.from(["A", "B", "C", "D"]) // Khởi tạo một Observable
    obj.subscribe( // Thực hiện subscribe Observable
      onNext: { data in
        print(data) // Nơi nhận dữ liệu của Observer được gửi đi từ Observable
      }, 
      onError: { error in
        print(error) // Nơi nhận error và Observable được giải phóng
      }, 
      onCompleted: {
        print("Completed") // Nhận được sự kiện khi Observable hoàn thành và Observable được giải phóng
      })
       .disposed()
    

    Kết quả trả về:

    A

    B

    C

    D

    Competed

    Subject

    Một đối tượng vừa có thể là Observable vừa có thể là Observer được gọi là Subject.

    Trong RxSwift cung cấp cho chúng ta 4 subject khác nhau với cách thứ hoạt động khác nhau đó là:

    • PublishSubject: Khởi đầu “empty” và chỉ emit các element mới cho subscriber của nó.
    • BehaviorSubject: Khởi đầu với một giá trí khởi tạo và sẽ relay lại element cuối cùng của chuỗi cho Subscriber mới.
    • ReplaySubject: Khởi tạo với một kích thước bộ đệm cố định, sau đó sẽ lưu trữ các element gần nhất vào bộ đệm này và relay lại các element chứa trong bộ đệm cho một Subscriber mới.
    • BehaviourReplay (which was Variable): Lưu trữ một giá trị như một state và sẽ relay duy nhất giá trị cuối cùng cho Subscriber mới.

    Để đi sâu vào từng loại subject mà RxSwift cung cấp khá là dài, nên mình chỉ lướt qua. Các bạn có thể tìm hiểu thêm, hoặc chờ một bài viết viết của mình đi sâu phân tích các subject trên nhé!

    Ở phần 1 mình đã giới thiệu qua khái niệm Reactive Programing, và một số thành phần chính của nó là Observabel và Observer, Subject.

    Các phần tiếp theo mình sẽ gửi tới các bạn ở phần 2 nhé.

    Cảm ơn các bạn đã đọc bài viết của mình.

    CongPQ

  • Sử dụng Enum trong Enum

    1. Enum là gì?

    An enumeration defines a common type for a group of related values and enables you to work with those values in a type-safe way within your code.

    Ở đây chúng ta sẽ lấy nguyên định nghĩa mà Apple đưa ra, hiểu một các nôm na thì Enum là kiểu dữ để định nghĩa một nhóm có giá trị liên quan, từ đó giúp bạn làm việc an toàn với kiểu dữ liệu đó trong code của bạn.

    2. Enum trong Enum

    Như tiêu đề, mình sẽ đi thẳng vào việc sử dụng Enum trong Enum, các trường    hợp sử   dụng enum thông thường các bạn có thể tìm hiểu ở các blog hoặc bài viết khác.

    • Bài toán đưa ra: Khi bạn có một struct như sau:

    ở đây chúng ta thấy có một enum để định về type của các PieChart. Câu hỏi đưa ra là khi bạn vẫn muốn tiếp tục sử dụng lại struct trên cho các object tương tự, và các object đó có type khác nhau thì hướng xử lí của bạn là như thế nào?

    -> Một trong các cách mà mình nghĩ các bạn sẽ dùng tới là sử dụng lại enum, thêm case hoặc là tạo một struct và enum mới như hình:

    Ở đây chúng ta quan sát giữa strcut ChartViewEntity với ChartViewEntityInWeek. Chúng ta thấy hai struct chỉ khác nhau về type( ở đây là khác nhau vê Enum). Và giữa 2 enum lại có mối quan hệ giống nhau.

    Câu hỏi đặt ra, tại sao chúng ta không sử dụng lại cùng 1 struct mà sửa lại Enum, và trong Enum của chúng ta sẽ chưa các case là type cho ChartView trong từng trường hợp mong muốn. Và chúng ta giải quyết bài toán trên bằng cách xử lí lại như sau:

    Ở đây mình đã tạo ra 3 enum, và enum PieChartType sẽ chứa 2 Enum còn lại ( đó là các Enum con phù hợp cho từng bài toán đặt ra riêng cho mỗi Object có các thuộc tính được trìu tượng qua struct ChartViewEntity). Vậy là bài toán của chúng ta đã được giải quyết, và chúng ta nhận ra rằng việc sử dụng Enum trong Enum có hiệu quả nhất định, giúp code của bạn gọn hơn, và trở nên tường minh hơn, đảm bảo sự an toàn khi bạn viết code.

    •  Qua ví dụ trên mình đã đưa ra cách giải quyết bài toán đồng thời là ứng dụng của việc sử dụng Enum trong Enum. Vậy tạo sao chúng ta không bắt tay vào sử dụng cho các bài toán riêng của mình. Chúc các bạn thành công.

    3. Tổng kết

    Trên đây mà một số chia sẻ về việc sử dụng Enum trong Enum nói riêng, và Enum nói chung. Với Swift chúng ta có Enum giúp cho việc viết code trở nên rõ ràng và rành mạch hơn. Mong rằng qua bài viết sẽ giúp ích cho các bạn phần nào đó về việc sử dụng enum và tiến tới con đường coder chuyên nghiệp hơn.

    Cảm ơn các bạn đã đọc bài viết của mình.

    Author: CongPQ