場景
最近專案中遇到一個問題,因為上傳圖片的後臺介面一次只支援上傳一張圖片,而使用者可以選擇多張圖片上傳,但是設計不是在使用者選擇完圖片就上傳了,專案要求是在無網路的情況下可以快取到本地的。這裡的快取,我設計了一個資料模型來儲存使用者輸入的內容,其中圖片是這個模型的一個陣列屬性。程式碼設計如下:
@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使用是挺好玩的,往後的有關網路程式設計中應該多考慮網路請求發出了,但是未知的回撥情況。