iOS 解决网络请求和界面刷新问题(GCD信号量实现)
功能要求瞬息万变,UI界面越来越多样化…(慢慢就习惯了,手动微笑ㄒoㄒ
) …开发中经常遇到,网络请求和界面刷新的问题解决方案。。。
考虑到大多数项目中都集成AFN, 本工程使用第三方库 AFN 作为网络请求方式。主要考虑功能,具体问题具体分析 ~
RequestTest
工程使用的是 Storyboard
快速开发、搭建的项目,目的是注重实现功能,主要是使用 GCD 的信号量 dispatch_semaphore_signal
效果在控制台中打印呈现 ㄒoㄒ
1. “一个任务需要等待另外一个任务执行完成后才可以执行” 问题解决
描述:
(1)网络请求。返回一个状态。(这是一个异步处理耗时操作,优化用户体验)
(2)人机交互。用户点击一个按钮,比如登录按钮,如果有登录状态,就执行任务3,如果没有,就等待任务1网络请求数据后,再继续执行任务3。
(3)通过点击按钮,想要执行的任务。
功能分解后主要实现
任务(1) :网络请求,请求成功后,数据调整。
任务(2) :按钮点击事件
#pragma mark -
- (IBAction)clickLogonButton:(id)sender {
NSLog(@"执行任务2 ...");
__weak __typeof__(self) weakSelf = self;
self.block = ^() {
[weakSelf doSomething];
};
if (_states && [_states isEqualToString:@"Logon"]) {
NSLog(@"状态正常");
self.block();
} else {
NSLog(@"状态异常");
// 若计数为0则一直等待
dispatch_semaphore_wait(self.lock, DISPATCH_TIME_FOREVER);
NSLog(@"等待状态修复 ... 再次请求");
[self request];
}
}
任务(3) :模拟一个执行的任务
- (void)doSomething {
NSLog(@"执行任务3: states = %@", self.states);
}
2. 一个页面有多个网络请求,等待所有请求完成后,刷新页面。
主要代码:
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self request1];
}) ;
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self request2];
}) ;
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self request3];
}) ;
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"刷新界面");
});
其中网络请求request
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[网络请求:{
成功:dispatch_semaphore_signal(sema);
失败:dispatch_semaphore_signal(sema);
}];
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
// 通过使用GCD中的信号量可以解决多个操作共用同一资源时, 造成主线程阻塞的问题.
3. 按照顺序执行网络请求操作,使用NSOperationQueue 依赖实现
例如:
(1)下载图片
(2)给图片添加水印
(3)保存图片
(4)主线程刷新UI界面
主要代码:
- (void)operationBlockTest {
// 处理图片的耗时操作在子线程中执行
NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"blockOp 下载图片 thread = %@",[NSThread currentThread]);
[self request:@"下载图片"];
}];
NSBlockOperation *blockOp1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"blockOp1 给图片添加水印 thread = %@",[NSThread currentThread]);
[self request:@"给图片添加水印"];
}];
NSBlockOperation *blockOp2 = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:2.0];
NSLog(@"blockOp2 保存图片 thread = %@",[NSThread currentThread]);
[self request:@" 保存图片"];
}];
// 给blockOp1添加依赖关系,使blockOp1在blcokOp执行结束后执行
[blockOp1 addDependency:blockOp];//也就是下载结束之后再给图片添加水印,然后保存图片。一种依赖关系
[blockOp2 addDependency:blockOp1];
// 创建队列(把上面要干的事情丢到队列中同时执行--有点类似GCD中的异步,并发,开启了多个线程)
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
// 添加到队列
[queue addOperation:blockOp];
[queue addOperation:blockOp1];
[queue addOperation:blockOp2];
// 设置队列中操作同时执行的最大数目,也就是说当前队列中呢最多由几个线程在同时执行,一般情况下允许最大的并发数2或者3
[queue setMaxConcurrentOperationCount:3];
// 队列中可以添加其他的 BlockOperation
for (int i = 0; i<50; i++) {
NSBlockOperation *blockOpp = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"blockOpp i = %d thread = %@",i,[NSThread currentThread]);
}];
[queue addOperation:blockOpp];
}
// 刷新UI的操作依赖关系必须在主线程中执行
NSBlockOperation *blocOpMain = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"blockOpMain 刷新UI 显示图片,thread = %@",[NSThread currentThread]);
}];
// 这两个操作的依赖关系,跨队列
[blocOpMain addDependency:blockOp2];
// 主队列
[[NSOperationQueue mainQueue] addOperation:blocOpMain];
}
4. 信号量实现的生产者消费者模式
GCD实现的生产者、消费者模式
#pragma mark - 生产者、消费者模式
/*
信号量实现生产者、消费者模式 GCD-信号量-实现方式
*/
- (void)producersAndConsumersMode{
__block int product = 0;
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //消费者队列
while (1) {
if(!dispatch_semaphore_wait(sem, dispatch_time(DISPATCH_TIME_NOW, DISPATCH_TIME_FOREVER))){
非 0的时候,就是成功的timeout了,这里判断就是没有timeout 成功的时候是 0
NSLog(@"消费%d产品",product);
product--;
};
}
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //生产者队列
while (1) {
sleep(1); //wait for a while
product++;
NSLog(@"生产%d产品",product);
dispatch_semaphore_signal(sem);
}
});
}