RxSwift các khái niệm cơ bản P.2
Bài đăng này đã không được cập nhật trong 8 năm
Getting Started
Trong bài viết này tôi sẽ tiếp tục nói về các khái niệm cơ bản của RxSwift
- Operators
- Custom operators
- Playgrounds
- Error handling
- Debugging Compile Errors
- Debugging
- Debugging memory leaks
operators
Có rất nhiều operators trong RxSwift. Danh sách có thể được tra cứu ở đây.
Custom operators
Có 2 cách để tự tạo custom operators.
Cách dễ
Tạo một operator mới thực chất là tạo observables. Cách tạo một map operator ( chưa được tối ưu hóa) .
extension ObservableType {
func myMap<R>(transform: E -> R) -> Observable<R> {
return Observable.create { observer in
let subscription = self.subscribe { e in
switch e {
case .next(let value):
let result = transform(value)
observer.on(.next(result))
case .error(let error):
observer.on(.error(error))
case .completed:
observer.on(.completed)
}
}
return subscription
}
}
}
Sử dụng map tự tạo:
let subscription = myInterval(0.1)
.myMap { e in
return "This is simply \(e)"
}
.subscribe(onNext: { n in
print(n)
})
Kết quả:
Subscribed
This is simply 0
This is simply 1
This is simply 2
This is simply 3
This is simply 4
This is simply 5
This is simply 6
This is simply 7
This is simply 8
...
Life happens
Khi gặp những vấn đề không thể giải quyết với custom operator bạn có thể thoát khỏi Rx monad, thực hiện lập trình imperative rồi đưa kết quả về Rx again Subjects..
let magicBeings: Observable<MagicBeing> = summonFromMiddleEarth()
magicBeings
.subscribe(onNext: { being in // thoát khỏi Rx monad
self.doSomeStateMagic(being)
})
.addDisposableTo(disposeBag)
let kitten = globalParty(
being,
UIApplication.delegate.dataSomething.attendees
)
kittens.on(.next(kitten)) // gửi kết quả về rx
let kittens = Variable(firstKitten) // trở về Rx monad
kittens.asObservable()
.map { kitten in
return kitten.purr()
}
// ....
Khi bạn thực hiện điều này, rất có thể có ai đó lại viết đoạn code dưới
kittens
.subscribe(onNext: { kitten in
// so something with kitten
})
.addDisposableTo(disposeBag)
Vì vậy, tốt nhất bạn không nên thực hiện.
Playgrounds
Nếu không chắc chắn về cách thức hoạt động của một operators, bạn có thể sử dụng playgrounds để kiểm tra lại.
Để sử dụng playgrounds hãy mở Rx.xcworkspace, build RxSwift-OSX scheme sau đó mở playgrounds trong Rx.xcworkspace.
Để hiển thị kết quả hãy mở Assistant Editor bằng cách click vào View > Assistant Editor > Show Assistant Editor
Error handling
Có 2 cơ chế quản lý lỗi.
-
Cơ chế Asynchronous error handling trong observables
Nếu một sequence kết thúc với lỗi, tất cả các sequence phụ thuộc cũng bị lỗi theo.
Để phục hồi lỗi của observable ta sử dụng catch operator. Khi sequence bị lỗi có thể sử dụng retry operator để thực hiện lại.
-
Debugging Compile Errors
Khi viết RxSwift/RxCocoa code, bạn có thể sử dụng compiler để suy ra loại của Observables. Đây là điểm hấp dẫn của Swift, nhưng đôi khi nó cũng gây ra phiền toái
images = word
.filter { $0.containsString("important") }
.flatMap { word in
return self.api.loadFlickrFeed("karate")
.catchError { error in
return just(JSON(1))
}
}
Nếu complier báo có lỗi đâu đó hãy thử thay đổi type trả về.
images = word
.filter { s -> Bool in s.containsString("important") }
.flatMap { word -> Observable<JSON> in
return self.api.loadFlickrFeed("karate")
.catchError { error -> Observable<JSON> in
return just(JSON(1))
}
}
Có thể tiếp tục thay đổi type cho đến khi tìm ra lỗi.
images = word
.filter { (s: String) -> Bool in s.containsString("important") }
.flatMap { (word: String) -> Observable<JSON> in
return self.api.loadFlickrFeed("karate")
.catchError { (error: NSError) -> Observable<JSON> in
return just(JSON(1))
}
Sau khi tìm ra lỗi hãy xóa type trả về này.
Debugging
Hãy sử dụng kết hợp debuger và debug operator. debug operator sẽ in tất cả các sự kiện và bạn còn có thể gắn nhãn cho các sự kiện này. Cách sử dụng debug:
let subscription = myInterval(0.1)
.debug("my probe")
.map { e in
return "This is simply \(e)"
}
.subscribe(onNext: { n in
print(n)
})
NSThread.sleepForTimeInterval(0.5)
subscription.dispose()
Kết quả:
[my probe] subscribed
Subscribed
[my probe] -> Event next(Box(0))
This is simply 0
[my probe] -> Event next(Box(1))
This is simply 1
[my probe] -> Event next(Box(2))
This is simply 2
[my probe] -> Event next(Box(3))
This is simply 3
[my probe] -> Event next(Box(4))
This is simply 4
[my probe] dispose
Disposed
Bạn cũng có thể tự tạo debug operator.
extension ObservableType {
public func myDebug(identifier: String) -> Observable<Self.E> {
return Observable.create { observer in
print("subscribed \(identifier)")
let subscription = self.subscribe { e in
print("event \(identifier) \(e)")
switch e {
case .next(let value):
observer.on(.next(value))
case .error(let error):
observer.on(.error(error))
case .completed:
observer.on(.completed)
}
}
return Disposables.create {
print("disposing \(identifier)")
subscription.dispose()
}
}
}
}
Debugging memory leaks
Trong chế độ debug Rx sẽ ghi lại tất cả tài nguyên được cấp phát trong một global variable resourceCount.
Trong trường hợp muốn tìm kiếm resource leak cách đơn giản nhất là in ra
RxSwift.resourceCount theo chu kỳ.
/* add somewhere in
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool
*/
_ = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
.subscribe(onNext: { _ in
print("Resource count \(RxSwift.resourceCount)")
})
Cách tốt nhất để test memory leaks:
- Di chuyển tới màn hình test
- Quay lại trước đó
- Ghi lại giá trị resource count ban đầu
- Di chuyển lần thứ 2 tới màn hình test.
- Quay lại trước đó
- Ghi lại giá trị resource count lần 2
Trong trường hợp có sự khác biệt của resource count giữa lần thứ nhất và lần thứ hai, có thể có memory leak.
Nguồn : RxSwift
All rights reserved