当用户点击图片时,图片应该被发送到他的邮件。我希望这在背景中发生,所以我使用SKPSMTPMessage。如果用户在上一个图片被发送/ Uploaded之前点击图片,我会收到错误。我想我会使用GCD序列化这个,但我无法解决它。
- (IBAction)snapStillImage:(id)sender
{
//code after taking the picture
if([uploadMethod isEqualToString:@"Mail"]){
time_t unixTime = (time_t) [[NSDate date] timeIntervalSince1970];
NSString *timestamp=[NSString stringWithFormat:@"%ld",unixTime];
NSData *jpgData = UIImageJPEGRepresentation(image, 0.5);
[jpgData writeToFile:[self documentsPathForFileName:timestamp] atomically:YES];
[self.imagesArray addObject:timestamp];
[self sendMail];
}
-(void)sendMail{
dispatch_queue_t myFifoQueue = dispatch_queue_create("com.xxxx.uploadpictures",DISPATCH_QUEUE_SERIAL);
if(!self.imagesArray.count == 0){
for(NSString *str in self.imagesArray){
dispatch_async(myFifoQueue,^{
[self sendMailWith:str];
});
}
}
}
答案 0 :(得分:5)
有几点意见:
正如Andrey指出的那样,您只想创建一个队列,而不是每次都创建一个新队列。
鉴于SKPSMTPMessage
异步操作,如果您希望它们按顺序运行,则需要重构此代码,以便下一个请求在完成先前的异步请求之前不会启动。典型的解决方案是:
使用操作队列(NSOperationQueue
)。
将异步发送请求包装在并发NSOperation
子类中。您已将NSOperation
设置为delegate
对象的SKPSMTPMessage
,并且在messageSent
和messageFailed
方法中,您将完成操作(发布必要的isFinished
和isExecuting
通知。
有关详细信息,请参阅并发编程指南的Operation Queues章节中的配置并发执行操作部分。
确保您配置队列或操作,以便按顺序发送它们(例如,将操作队列的maxConcurrentOperationCount
设置为1以使其成为串行队列。)
为了说明,您需要定义一个操作队列:
@property (nonatomic, strong) NSOperationQueue *mailQueue;
您需要配置一次:
self.mailQueue = [[NSOperationQueue alloc] init];
self.mailQueue.maxConcurrentOperationCount = 1;
然后您将邮件请求添加到该队列:
[self.mailQueue addOperation:[[SendEmailOperation alloc] initWithTo:@"rob@example.com"
subject:@"title"
body:@"body"
path:pathToImage]];
所以,有趣的问题是这个SendMailOperation
可能是什么样的:
// SendEmailOperation.h
#import "ConcurrentOperation.h"
@interface SendEmailOperation : ConcurrentOperation
@property (nonatomic, copy) NSString *to;
@property (nonatomic, copy) NSString *subject;
@property (nonatomic, copy) NSString *body;
@property (nonatomic, copy) NSString *path;
- (instancetype)initWithTo:(NSString *)to subject:(NSString *)subject body:(NSString *)body path:(NSString *)path;
@end
和
// SendEmailOperation.m
#import "SendEmailOperation.h"
#import "SKPSMTPMessage.h"
@interface SendEmailOperation () <SKPSMTPMessageDelegate>
@end
@implementation SendEmailOperation
- (instancetype)initWithTo:(NSString *)to subject:(NSString *)subject body:(NSString *)body path:(NSString *)path
{
self = [super init];
if (self) {
self.to = to;
self.subject = subject;
self.body = body;
self.path = path;
}
return self;
}
- (void)main
{
SKPSMTPMessage *message = [self createMessage]; // configure your message like you are now
message.delegate = self;
[message send];
}
- (SKPSMTPMessage *)createMessage
{
SKPSMTPMessage *message = [[SKPSMTPMessage alloc] init];
// configure this message like you are now, picking up the various class properties
// (e.g. to, subject, body, etc.).
return message;
}
#pragma mark - SKPSMTPMessageDelegate
-(void)messageSent:(SKPSMTPMessage *)message
{
[self completeOperation];
}
-(void)messageFailed:(SKPSMTPMessage *)message error:(NSError *)error
{
NSLog(@"%s: %@", __PRETTY_FUNCTION__, error);
[self completeOperation];
}
@end
最后,并发操作子类的内容在此ConcurrentOperation
类中被捕获。一旦你开始使用并发操作,你会发现自己经常使用这个模式,所以这个类让你不必经常重新实现并发操作的子类:
// ConcurrentOperation.h
#import <Foundation/Foundation.h>
@interface ConcurrentOperation : NSOperation
- (void)completeOperation;
@end
和
// ConcurrentOperation.m
#import "ConcurrentOperation.h"
@interface ConcurrentOperation ()
@property (nonatomic, getter = isFinished, readwrite) BOOL finished;
@property (nonatomic, getter = isExecuting, readwrite) BOOL executing;
@end
@implementation ConcurrentOperation
@synthesize finished = _finished;
@synthesize executing = _executing;
- (id)init
{
self = [super init];
if (self) {
_finished = NO;
_executing = NO;
}
return self;
}
- (void)start
{
if ([self isCancelled]) {
self.finished = YES;
return;
}
self.executing = YES;
[self main];
}
- (void)completeOperation
{
self.executing = NO;
self.finished = YES;
}
#pragma mark - NSOperation methods
- (BOOL)isConcurrent
{
return YES;
}
- (void)setExecuting:(BOOL)executing
{
if (_executing != executing) {
[self willChangeValueForKey:@"isExecuting"];
_executing = executing;
[self didChangeValueForKey:@"isExecuting"];
}
}
- (void)setFinished:(BOOL)finished
{
if (_finished != finished) {
[self willChangeValueForKey:@"isFinished"];
_finished = finished;
[self didChangeValueForKey:@"isFinished"];
}
}
@end
我知道这看起来很多,但是一旦你的工具带中有ConcurrentOperation
(或类似的东西),你会发现创建并发操作非常简单,并且无论何时你想要它都非常有用顺序执行一系列异步任务(或与某些特殊依赖项同时执行)。
答案 1 :(得分:3)
现在编写的代码每次调用sendMail
方法时都会创建一个新队列,并在此新队列上提交一个块,因此不会执行串行执行。
要修复此问题,您可以将myFifoQueue
设为实例变量(例如,在init
方法中创建),或将其设为静态,例如:
-(void)sendMail {
dispatch_queue_t myFifoQueue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
myFifoQueue = dispatch_queue_create(...);
}
if(!self.imagesArray.count == 0) {
...
}
}