在Objective-C中同步异步任务

时间:2014-10-28 19:47:31

标签: objective-c asynchronous grand-central-dispatch synchronous gcdasyncsocket

我试图使用GCDAsyncSocket向我的iDevice客户端发送一些图像文件(差不多100MB)。

我想同步向客户端发送数据包。我的意思是在向第一个客户端发送100MB数据后迭代到下一个客户端。但由于GCDAsyncSocket的异步特性,我不知道如何序列化这些数据包发送。

我无法使用信号量,因为在发送图像之前,我会与每个客户协商以了解我应该发送的图像,然后尝试发送这些图像。而且我找不到一种干净的方式来等待信号并发出信号。

- (void)sendImagesToTheClients:clients
{
    ...
    //negotiating with client to know which images should sent
    ...

    for(Client* client in clients)
    {
       packet = [packet addImages: images];
       [self sendPacket:packet toClient:client];
    }
}

- (void)sendPacket:packet toClient:client
{
 // Initialize Buffer
    NSMutableData *buffer = [[NSMutableData alloc] init];
    NSData *bufferData = [NSKeyedArchiver archivedDataWithRootObject:packet];

    uint64_t headerLength = [bufferData length];
    [buffer appendBytes:&headerLength length:sizeof(uint64_t)];
    [buffer appendBytes:[bufferData bytes] length:[bufferData length]];

    // Write Buffer
    [client.socket writeData:buffer withTimeout:-1.0 tag:0];
}

这是AsyncSocket写入数据的工作原理:

    - (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag
{
    if ([data length] == 0) return;

    GCDAsyncWritePacket *packet = [[GCDAsyncWritePacket alloc] initWithData:data timeout:timeout tag:tag];

    dispatch_async(socketQueue, ^{ @autoreleasepool {

        LogTrace();

        if ((flags & kSocketStarted) && !(flags & kForbidReadsWrites))
        {
            [writeQueue addObject:packet];
            [self maybeDequeueWrite];
        }
    }});

    // Do not rely on the block being run in order to release the packet,
    // as the queue might get released without the block completing.
}

所有人都知道如何同步这项任务?

更新

用于套接字连接我使用GCDAsyncSocket大量使用委托进行事件通知。(GCDAsyncSocket.h和GCDAsyncSocket.m)(没有使用completionHandler的方法)。

我编写了一个名为TCPClient的类,它处理套接字连接和数据包发送,并将其设置为初始化套接字的委托。 写入数据包后,调用委托方法- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag。这只会告诉我一些数据。在这里,我无法根据书面数据决定拨打dispatch_group_leave。所以依赖委托方法是没用的。

我在GCDAsyncSocket.h和.m文件中修改了[client.socket writeData:buffer withTimeout:-1.0 tag:0]以接受completionBlock:[client.socket writeData:buffer withTimeout:-1.0 tag:0 completionBlock:completionBlock] 使用这种方法可以帮助我解决同步异步任务。

// perform your async task
            dispatch_async(self.socketQueue, ^{
                [self sendPacket:packet toClient:client withCompletion:^(BOOL finished, NSError *error)
                 {
                     if (finished) {
                         NSLog(@"images file sending finished");
                         //leave the group when you're done
                         dispatch_group_leave(group);
                     }
                     else if (!finished && error)
                     {
                         NSLog(@"images file sending FAILED");
                     }

                 }];

但问题是在更新GCDAsyncsocket后,我的代码可能会中断。 在这里,我正在寻找简洁的方法来向GCDAsyncsocket添加完成处理程序,而无需直接修改它。比如在它周围创建一个包装器或使用objective-c运行时的功能。 你有什么想法吗?

1 个答案:

答案 0 :(得分:4)

您可以使用调度组完成此操作。对于具有完成块的异步任务:

//create & enter the group
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);

// perform your async task
dispatch_async(socketQueue, ^{

    //leave the group when you're done
    dispatch_group_leave(group);
});

// wait for the group to complete 
// (you can use DISPATCH_TIME_FOREVER to wait forever)
long status = dispatch_group_wait(group,
                    dispatch_time(DISPATCH_TIME_NOW,NSEC_PER_SEC * COMMAND_TIMEOUT));

// check to see if it timed out, or comepleted
if (status != 0) {
    // timed out
}

或者对于具有委托的任务:

@property (nonatomic) dispatch_group_t group;

-(BOOL)doWorkSynchronously {

    self.group = dispatch_group_create();
    dispatch_group_enter(self.group);

    [object doAsyncWorkWithDelegate:self];

    long status = dispatch_group_wait(self.group,
                        dispatch_time(DISPATCH_TIME_NOW,NSEC_PER_SEC * COMMAND_TIMEOUT));

    // A
    if (status != 0) {
        // timed out
        return NO
    }

    return YES;
}

-(void)asyncWorkCompleted {}

    // after this call, control should jump back to point A in the doWorkSynchronously method
    dispatch_group_leave(self.group);
}