1. 程式人生 > >iOS開發筆記之四十七——多執行緒場景下的KVO使用參考方案

iOS開發筆記之四十七——多執行緒場景下的KVO使用參考方案

如果你取檢索網路資料會發現,有人直接不建議把KVO與多執行緒混合使用,因為KVO的響應和KVO觀察的值變化是在一個執行緒上的,不同的執行緒可能會導致不可預知的後果。參考資料見這裡:

當然,場景總是千變萬化的,下面我就介紹一種多執行緒下使用KVO的場景。

具體場景如下:

(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;
                });

這種方法可以達到與方案二相同的效果。