iOS Concurrency - Phần 5: Sử dụng Grand Central Dispatch hay Operation?

Sử dụng Grand Central Dispatch hay Operation?

Câu hỏi thường gặp

Em sử dụng GCD hay Operation cho việc hiện thực concurrency (multi-threading)? - Có lẻ đây là câu hỏi muôn thuở khi chúng ta đi phỏng vấn iOS. Ngày hôm nay mình sẽ trả lời ngắn gọn chúng trong blog này để các bạn có thể tự tin trả lời với nhà tuyển dụng để từ đó deal được lương với mức giá cao hơn thay vì trả lời rằng “Em thấy sài cái nào cũng được”. Bên cạnh đó, việc phân biệt khi nào sài chúng sẽ giúp bạn tạo ra được app với thời gian ngắn hơn và performance cao hơn. Operation và OperationQueue được Apple giới thiệu từ iOS2, trong khi GCD được Apple giới thiệu từ iOS4. Tựu chung cả hai đều đóng gói những task lại và dispatch chúng trên những thread khác nhau cho phù hợp. Chính vì cả hai đều phục vụ cùng một mục đích như vậy nên nhiều developer cảm thấy bối rối khi sử dụng. Mình sẽ so sánh 2 bộ API này ở bài viết này.

Những gì làm chúng khác nhau?

GCD là API cấp độ thấp C mà nó làm việc trực tiếp với cấp độ Unix của hệ thống. Trong khi đó Operation là API cấp độ trừu tượng cao hơn GCD, nó gần gũi với ngôn ngữ tự nhiên hơn, dễ sử dụng hơn, nhiều tính năng hơn. Bên cạnh đó, Operation hoạt động trên nền tảng của GCD. Điều này đồng nghĩa rằng khi bạn sử dụng Operation thì bạn cũng ngầm định sử dụng GCD ở bên dưới. Do đó cùng một tác vụ đơn giản nếu bạn sử dụng GCD thì sẽ nhanh hơn sử dụng Operation bởi vì GCD gần với cấp độ hệ thống hơn.

Những lợi ích của Operation mà GCD không có

Bởi vì Operation được xây dựng dựa trên GCD do đó Operation không chỉ có những thứ mà GCD cung cấp mà nó còn có thêm những thứ mới mà giúp cho việc lập trình nhanh chóng tiện lợi hơn.

Dependencies (Sự phụ thuộc) Operation API hỗ trợ dependency một cách dễ dàng. Điều này giúp bạn chạy những operation theo một thứ tự cho trước. Bạn có thể set một operation chỉ được chạy khi một operation khác chạy xong. Ví dụ như bạn muốn download hình ảnh từ internet về và apply filter để chỉnh sửa bức hình. Bạn sẽ chia công việc phức tạp trên thành 2 operation: 1 operation dùng để xử lý vấn đề networking liên quan đến download hình ảnh, operation còn lại dùng để filter chỉnh sửa hình ảnh (việc chia thành 2 operation giúp cho việc reuse sau này). Bạn không thể filter hình ảnh khi hình chưa download xong, do đó filter operation phụ thuộc vào download operation. Bạn có thể hiện thực bằng đoạn code mẫu dưới đây

let downloadOperation: Operation = ...
let filterOperation: Operation = ...
filterOperation.addDependency(downloadOperation)
    
let operationQueue = OperationQueue.main
operationQueue.addOperations([downloadOperation, filterOperation], waitUntilFinished: false)

Mặc dù bạn có thể hiện thực Dependencies trong GCD bằng cách sử dụng DispatchGroup như mình trình bày ở những bài trước, nhưng việc dùng Operation tiện lợi hơn rất nhiều.

Khả năng giám sát trạng thái (Observable state) Lớp Operation và OperationQueue có một số lượng những thuộc tính được dùng để quản lý trạng thái (state) của operation và operationqueue. Những trạng thái có thể có là: pending, ready, executing, finished và cancelled. Mối quan hệ của chúng được trình bày bằng hình dưới đây:

Pause, Cancel và Resume Operation cho phép chúng ta thực hiện pause, cancel và resume. Đối với GCD, khi bạn dispatch một task, bạn sẽ không còn kiểm soát bên trong việc chạy một task (mặc dù bạn chỉ có thể cancel một task một cách phức tạp). Tuy nhiên với Operation, API cho phép bạn kiểm soát, điều chỉnh vòng đời của một Operation.

Những thứ khác: OperationQueue cho phép chúng ta đặc tả số lượng operation tối đa chạy đồng thời trên queue. Điều này giúp chúng ta dễ dàng kiểm soát được số lượng operation chạy đồng thời hay để tạo một operation queue tuần tự (serial).

var queue = NSOperationQueue()
queue.name = "Image Filtration queue"
queue.maxConcurrentOperationCount = 1

Sử dụng cái nào?

Apple khuyên chúng ta nên sử dụng cấp độ trừu tượng cao nhất có thể. Nếu áp dụng lời khuyên này thì Operation sẽ là sự lựa chọn đầu tiên. Nhưng tại sao Apple khuyên chúng ta sử dụng mức độ trừu tượng cao nhất? Với mỗi lần release phiên bản iOS mới, Apple tối ưu hoá framework và thư viện nhằm tăng cường sức mạnh của hệ điều hành. Và điều này thường thay đổi tới những API cấp độ thấp. Mặc dù những API cấp độ cao được xây dựng trên những API cấp độ thấp thay đổi ít thường xuyên hơn, chúng ta vẫn có lợi từ việc Apple nâng cấp những API cấp độ thấp mà chúng được xây dựng trên nó.

Khi nào sử dụng GCD? GCD là một sự lựa chọn tuyệt vời khi bạn chỉ muốn dispatch một block code tới main queue hay global queue (serial queue hay concurrent queue). Nếu bạn không muốn tạo một lớp con từ Operation để xử lý một tác vụ bình thường thì GCD cũng là một lựa chọn tuyệt vời.

Khi nào sử dụng Operation? Mình đã phân tích những thứ mà Operation có mà GCD không có ở trên. Do đó các bạn cũng có thể rõ được khi nào sử dụng Operation. API này là một lựa chọn tuyệt vời cho việc đóng gói những đoạn code theo chức năng, quản lý dependencies hay trạng thái của operation.

Kết hợp cả hai Bằng những điểm mạnh và điểm yếu của từng API mà mình phân tích ở trên, việc kết hợp sử dụng chúng luôn trong mỗi hoàn cảnh cụ thể luôn là một sự lựa chọn tốt cho mọi project.

Kết thúc chuỗi bài viết về iOS Concurrency

Thông qua bài phân tích nho nhỏ này, hy vọng các bạn có thể hiểu được khi nào sử dụng Operation và GCD để có thể trả lời những câu hỏi phỏng vấn cũng như áp dụng vào trong project. Và đây là bài cuối cùng trong chuỗi những bài viết về iOS Concurrency của mình. Hẹn gặp các bạn ở những bài viết kế tiếp của mình.