iOS for 循环内网络请求的处理

2,216 阅读3分钟

场景

最近项目中遇到一个问题,因为上传图片的后台接口一次只支持上传一张图片,而用户可以选择多张图片上传,但是设计不是在用户选择完图片就上传了,项目要求是在无网络的情况下可以缓存到本地的。这里的缓存,我设计了一个数据模型来存储用户输入的内容,其中图片是这个模型的一个数组属性。代码设计如下:

@interface AMGOfflineModel : AMGBaseModel

@property (nonatomic, copy) NSString *md5String;
@property (nonatomic, copy) NSString *attId;    
@property (nonatomic, copy) NSString *contractBidSectionId; 
@property (nonatomic, copy) NSString *division; 
@property (nonatomic, copy) NSString *mainId;  
@property (nonatomic, copy) NSString *planImplementId; 
@property (nonatomic, copy) NSString *questionDesc; 
@property (nonatomic, copy) NSString *questionQuickDesc;    
@property (nonatomic, copy) NSString *questionSource;  
@property (nonatomic, copy) NSString *questionType;
@property (nonatomic, strong) NSArray<AMGCacheImageModel *> *photos;
@property (nonatomic, strong) AMGInfoDictModel *dictModel;
@property (nonatomic, assign) BOOL isSyn;

@end

photos就是用户选择的图片,这里考虑到某些情况,就设计了一个模型来封装图片数据,如下:

@interface AMGCacheImageModel : AMGBaseModel

@property (nonatomic, strong) UIImage *image;
@property (nonatomic, copy) NSString *filename;
@property (nonatomic, copy) NSString *attId;
@property (nonatomic, assign) NSInteger index;

@end

attId是图片上传成功后,后台接口返回的文件id,后面上传整体数据时需要用到的。 本地缓存的数据是一个数组,数组里面的元素是AMGOfflineModel,用户保存的数据都会存到这个数组中,有网络时,只有一个同步按钮将本地数据上报到线上。

保存多个内容,每个内容有多张图片

处理数据上报

  • 首先将一个内容中的图片附件逐个上传完成后,返回所得到的attId,注意这里需要在附件都上传完后再可以返回,假如有某个附件上传失败,则当前内容不上报。这时候怎么知道所有附件上传过了呢?而且怎么判断是否成功呢?直接上代码:

  • 1)主要使用的GCD的队列组,当队列组里面的操作都完成后,会有一个通知,我们统一在接收到这个通知后处理返回数据。但是这样还不够,单纯有队列组只能确定里面的上传附件操作都请求上传接口了,不能确保所有请求操作都返回了才通知,这就有可能附件都还没有上传完成,就执行了通知里面的操作。
  • 2)为了解决上面出现的问题,使用到了dispatch_semaphore_t semaphore = dispatch_semaphore_create(0)信号,这里的信号就比如线程的加锁,当信号值为0时就会让当前线程停留在dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)方法处,直到调用dispatch_semaphore_signal(semaphore)方法加1,此时通知dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)方法减1,然后继续往下执行。
  • 3)看着代码,再想一下,是不是在所有上传附件请求都有返回时,才会执行通知里的操作。
  • 4)一个内容的附件上传完成,接来就要将内容上报了。上代码:

  • 5)从上面代码看到,其实逻辑是和附件上传是一样的,只不过同步内容信号是添加到封装的网络请求saveQuestionWithAttId方法中了,还是要等待一个内容上报成功后才会继续走for中的下一次循环。

总结

dispatch_semaphore_t 配合dispatch_group_t使用是挺好玩的,往后的有关网络编程中应该多考虑网络请求发出了,但是未知的回调情况。