如果你取检索网络资料会发现,有人直接不建议把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;
});
这种方法可以达到与方案二相同的效果。