iOS · reactive programming · ReactiveX

Subject và Variable trong RxSwift (Reactive Programming)

Trong các bài trước, mình đã giới thiệu về Stream, Observable, Operator và Scheduler. Trong phần này, mình sẽ giới thiệu về Subject và Variable, các thành phần đóng vai trò như những cầu nối giữa Rx và các mô hình, thư viện không sử dụng Rx.

1. Subject

Trong Reactive Programming, Observable là nơi khởi nguồn, là gốc của  mọi stream.Nhưng có những trường hợp, chúng ta không thể đơn thuần bọc một đối tượng bên trong một Observable, sau đó phát ra những tín hiệu.

Chẳng hạn khi sử dụng UIImagePickerController, ngoài việc quan tâm tới các hình ảnh mà người dùng chọn (stream), ứng dụng cần tương tác với chính UIImagePickerController để ẩn, hiển, … như vậy không thể bọc UIImagePickerController bên trong Observable. Khi đó, Subject sẽ đóng vai trò cầu nối, giúp chuyển đổi các tương tác của người dùng thành các streams tương ứng.

Lưu ý: Thư viện RxCocoa đã cung cấp rất nhiều extension cho các UIControl, nên trước khi sử dụng Subject, hãy kiểm tra lại những extension trong RxCocoa.

1.1. PublishSubject

Chúng ta cùng xem xét một ví dụ sử dụng PublishSubject để chuyển đổi hình ảnh được người dùng chọn thành một stream.

Một đặc điểm của PublishSubject là các phần tử có thể được phát ngay sau khi Subject được khởi tạo, bất chấp chưa có đối tượng nào subscribe tới nó (hot observable). Observer sẽ không nhận được các phần tử phát ra trước thời điểm subscribe. Tương tự, Observer cũng không nhận được các phần tử được phát ra sau khi có tín hiệu lỗi. Ví dụ sau kết quả sẽ in ra: 2, 3. Bởi vì 1 phát ra trước khi subscribe và 4 được phát ra sau khi error.

1.2. BehaviorSubject

BehaviorSubject có cơ chế hoạt động gần giống với PublishSubject, nhưng Observer sẽ nhận được giá trị mặc định hoặc giá trị ngay trước thời điểm subscribe. Observer sẽ nhận được ít nhất một giá trị.

Chẳng hạn, nếu coi việc cuộn thanh trượt của UIScrollView là một stream (offset là giá trị của các phần tử trong stream), thì ngay khi subscribe vào stream, chúng ta cần biết vị trí offset hiện tại của UIScrollView, do vậy chúng ta cần sử dụng BehaviorSubject

s-behaviorsubject

Để khởi tạo BehaviorSubject, chúng ta cần cung cấp giá trị mặc định (phần tử màu hồng trên sơ đồ).

Observer 1 subscribe khi chưa có phần tử nào được phát ra, khi đó nó sẽ nhận được giá trị mặc định và những giá trị sau thời điểm subscribe (màu đỏ, xanh lá, xanh lam).

Observer 2 subscribe sau khi các phần tử màu đỏ và xanh lá được phát, nên sẽ nhận được phần tử màu xanh (ngay trước thời điểm subscribe) và phần tử màu xanh

1.3. ReplaySubject

ReplaySubject tương tự như BehaviorSubject nhưng thay phát thêm duy nhất một phần tử trước đó, ReplaySubject cho phép developer chỉ định số lượng phần tử tối đa được phát lại khi subscribe. Ngoài ra, khi khởi tạo ReplaySubject, chúng ta không cần khai báo giá trị mặc định như BehaviorSubject.

Kết quả in ra 2, 3, 4, 5, 6, 7, bao gồm 5 phần trước khi subscribe và một phần tử sau khi subscribe. Lưu ý: ở ví dụ này ReplaySubject sẽ phát lại tối đa 5 phần tử. Giả sử chúng ta chỉ phát ra 3 phần tử trước khi subscribe, thì nó vẫn được phát lại mà không chờ đủ 5 phần tử.

Ở ví dụ trên nếu có error ngay sau khi phát phần tử 4 chẳng hạn thì kết quả là: 1, 2, 3, 4 và stream bị kết thúc.

1.4. Sử dụng Subject nâng cao

Với các Control mà RxCocoa không hỗ trợ, các bạn có thể sử dụng Subject trực tiếp như các ví dụ trên. Nhưng việc sử dụng như trên không có tính tái sử dụng. Ngoài ra, chúng ta vẫn phải khai báo các delegate như cách thông thường. Trong buổi meetup gần nhất về Rx, mình nhận được một câu hỏi là: Có cách nào sử dụng Rx cho UIImagePickerController(và các control không được RxCocoa hỗ trợ) một cách Reactive không?

Câu trả lời là các bạn hoàn toàn có thể hiện thực những extension giống RxCocoa như ví dụ sau. Với cách này, các bạn có thể tái sử dụng những extension này ở mọi nơi, không cần hiện thực các delegate khi sử dụng.

Trước hết, định nghĩa một DelegateProxy. Phần quan trọng nhất là thực hiện các hàm của UIImagePickerControllerDelegate

Phần tiếp theo, định nghĩa Rx extensions cho UIImagePickerController. Công việc chính là public các Observable của Proxy phía trên

Xong. Chúng ta hoàn toàn có sử dụng UIImagePickerController như các extension khác của RxCocoa.

3. Kết

Mình vừa giới thiệu về Subject và Variable trong RxSwift. Chúng là những cầu nối giữa mô hình Imperative với Declarative trong RxSwift. Vì thế, chúng ta nên sử dụng Subject đúng như mục đích nó sinh ra. Khi sử dụng Subject nó có nhược điểm sau:

  • Việc cho phép onNext tại nhiều nơi thay vì tập trung tại trong nội tại như Observable khiến khó maintain hơn
  • Không đảm bảo thread-safety: nếu onNext được gọi trên các thread khác nhau sẽ vi phạm Observable Contract
  • Hot Observable: Observer có thể bỏ qua một số phận tử trước khi subscribe (không nên sử dụng các toán tử max, min, sum, … cho Subject)

Tóm lại, khi nào nên sử dụng Subject? Câu trả lời: Khi không có lựa chọn khác.

 

Trả lời

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Đăng xuất /  Thay đổi )

Google photo

Bạn đang bình luận bằng tài khoản Google Đăng xuất /  Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Đăng xuất /  Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Đăng xuất /  Thay đổi )

Connecting to %s