iOS开发笔记之四十七——多线程场景下的KVO使用参考方案

来源:互联网 发布:资产抵押债券知乎 编辑:程序博客网 时间:2024/05/20 09:45

如果你取检索网络资料会发现,有人直接不建议把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;                });

这种方法可以达到与方案二相同的效果。






0 0
原创粉丝点击