使用GCD以串行顺序发送邮件

时间:2014-08-21 12:28:28

标签: ios objective-c

当用户点击图片时,图片应该被发送到他的邮件。我希望这在背景中发生,所以我使用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];
        });
    }
}
}

2 个答案:

答案 0 :(得分:5)

有几点意见:

  1. 正如Andrey指出的那样,您只想创建一个队列,而不是每次都创建一个新队列。

  2. 鉴于SKPSMTPMessage异步操作,如果您希望它们按顺序运行,则需要重构此代码,以便下一个请求在完成先前的异步请求之前不会启动。典型的解决方案是:

    • 使用操作队列(NSOperationQueue)。

    • 将异步发送请求包装在并发NSOperation子类中。您已将NSOperation设置为delegate对象的SKPSMTPMessage,并且在messageSentmessageFailed方法中,您将完成操作(发布必要的isFinishedisExecuting通知。

      有关详细信息,请参阅并发编程指南Operation Queues章节中的配置并发执行操作部分。

    • 确保您配置队列或操作,以便按顺序发送它们(例如,将操作队列的maxConcurrentOperationCount设置为1以使其成为串行队列。)


  3. 为了说明,您需要定义一个操作队列:

    @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) {
        ...
    }
}