如果你取檢索網絡資料會發現,有人直接不建議把KVO與多線程混合使用,因爲KVO的響應和KVO觀察的值變化是在一個線程上的,不同的線程可能會導致不可預知的後果。參考資料見這裏:
(1)http://objccn.io/issue-7-3/
(2)http://blog.csdn.net/hello_hwc/article/details/43815743
當然,場景總是千變萬化的,下面我就介紹一種多線程下使用KVO的場景。
具體場景如下:
(1)主線程裏面請求數據,數據是一個類似http://www.dpfile.com/sc/appskin/20160309163700.zip的鏈接;
(2)數據拿到後,建立一個子線程,線程去請求zip包數據,並解壓縮到本地(圖片.png文件);
(3)通知(KVO)主線程,去本地目錄取圖片資源,刷新UI;
具體代碼實現如下
1、方案一:
(1)主線程viewDidLoad方法中,註冊監聽,併發送數據請求;
@weakify(self)
[RACObserve(self, enablePromotionSkin)subscribeNext:^(NSNumber *enablePromotionSkin) {
@strongify(self);
if ([[self enablePromotionSkin] integerValue])
{
//...刷新UI
}
}];
(2)子線程中,解壓縮並通知主線程刷新UI;
- (RACSignal *)fetchSkinSignalWithConfig:(NVModelBaseOSAppPromoDo *)skinConfig
{
return (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
//下載、解壓縮操作
//...
//md5校驗
if ([[responseData md5] isEqualToString:skinConfig.md5])
{
self.enablePromotionSkin = @(YES); //通知主線程
}
});
}];
}
這樣的調用KVO是直接跨越線程,實際運行時發現,最終從主線程發送請求,到圖片刷出來,會經歷5-10s的時間,zip包沒有超過500k,並且wifi網絡下,這個真的好慢,沒有人能容忍。
KVO消息從子線程發出,到主線程上響應,而nonatomic屬性的enablePromotionSkin是非線程安全的,這樣設計會導致不可預知的問題。
2、方案二:
RAC本身有比較強大機制可以處理這種異步場景:
(1)主線程註冊時,強制指定了主線程處理消息;
@weakify(self)
[[RACObserve(self, enablePromotionSkin) deliverOn:[RACScheduler mainThreadScheduler]] subscribeNext:^(NSNumber *enablePromotionSkin) {
@strongify(self);
if ([self enablePromotionSkin] integerValue)
{
//...刷新UI
}
}];
(2)子線程處理時,強制由主線程處理;
- (RACSignal *)fetchSkinSignalWithConfig:(NVModelBaseOSAppPromoDo *)skinConfig
{
return [RACSignal startEagerlyWithScheduler:[RACScheduler mainThreadScheduler] block:^(id<RACSubscriber> subscriber) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
//....
});
}];
}
這樣之後,圖片的下載一般會在2s左右就會刷新出來,基本達到了預期效果;
3、方案三:
如果不用RAC提供的機制,我們也可以採取直接主線程發消息的方法:
dispatch_async(dispatch_get_main_queue(), ^{
self.enablePromotionSkin = YES;
});
這種方法可以達到與方案二相同的效果。