等待异步块完成 - 信号(似乎)不起作用

时间:2015-10-30 13:51:40

标签: xcode macos callback objective-c-blocks semaphore

我正在使用cryptotokenkit从智能卡发送/接收数据。用例是,在我做其他事情之前,我必须得到卡片API的响应。

在我的情况下,我发现在kMaxBlockingTimeSmartCardResponse(= 10)秒后总是调用此行。 来自

dispatch_semaphore_t sema = dispatch_semaphore_create(0);

直接转到

dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kMaxBlockingTimeSmartCardResponse * NSEC_PER_SEC));
        dispatch_semaphore_wait(sema, timeout);

然后等待10秒钟来阻止通话。它不是块回调延迟。如果我将调度时间设置为20或30秒,则等待20或30秒执行。等待调用确实等待指定的时间,然后执行回调块。

我做错了什么?我想知道这是否与adObserver有关。

- (void) updateSlots {
    self.slotNames = [self.manager slotNames];
    self.slotModels = [NSMutableArray new];
    self.slots = [NSMutableArray new];

    if([self.slotNames count] > 0)
    {
        for (NSString *slotName in self.slotNames)
        {
            NSLog(@"SmartCard reader found: %@", slotName);
            // semaphore BLOCK starts
            dispatch_semaphore_t sema = dispatch_semaphore_create(0);
            [self.manager getSlotWithName:slotName reply:^(TKSmartCardSlot *slot)
             {
                 if (slot) {
                     SCSlotModel *slotModel = [SCSlotModel new];
                     [self.slotModels addObject:slotModel];

                     [self.slots addObject:slot];
                     [slot addObserver:self forKeyPath:@"state"
                               options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld | NSKeyValueObservingOptionInitial
                               context:nil];

                     slotModel.suffixedName = slotName; // NOT slot.name; original name is suffixed with 01, 02 etc.
                     slotModel.slot = slot;
                     slotModel.cardStatus = [CardUtil mapCardStatus:slot.state];
                     DLog(@"slot: %@, slotmodel: %@",slot.name, slotModel);
                 } else {
                     NSLog(@"Did not find slot with name: %@", slotName);
                 }
                 dispatch_semaphore_signal(sema);
             }];
            dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kMaxBlockingTimeSmartCardResponse * NSEC_PER_SEC));
            dispatch_semaphore_wait(sema, timeout);
            // semaphore BLOCK ends
            self.errorCode = SCPNoError;
        }

    } else {
        NSLog(@"No slot available.");
        self.errorCode = SCPErrorReaderNotFound;
    }
}

getSlotWithName是来自TKSmartCardSlotManager

的方法
/// Instantiates smartcard reader slot of specified name.  If specified name is not registered, returns nil.
- (void)getSlotWithName:(NSString *)name reply:(void(^)(TKSmartCardSlot *__nullable slot))reply;

但在其他地方,它可以按预期工作,用于相同类型的异步调用。

- (BOOL) beginSession:(TKSmartCard *)card
{
    __block BOOL response = NO;
    dispatch_semaphore_t sema = dispatch_semaphore_create(0);
    [card beginSessionWithReply:^(BOOL success, NSError *error) {
        response = success;
        if (!success) {
            NSLog(@"Could not begin session.");
        }
        if (error) {
            NSLog(@"Could not begin session with error %@", error);
        }
        dispatch_semaphore_signal(sema);
    }];

    dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kMaxBlockingTimeSmartCardResponse * NSEC_PER_SEC));
    dispatch_semaphore_wait(sema, timeout);
    return response;
}

1 个答案:

答案 0 :(得分:0)

getSlotWithName:reply:重新调用其回复处理程序的线程是什么? 例如,如果在主线程上执行updateSlots,并且您正在执行以下操作:

... // some async operation in `getSlotWithName:reply:` has completed
dispatch_async(dispatch_get_main_queue(), ^{
    reply(slot)
});

然后你最终会在主线程上排队你的回复,但主线程目前已被你的信号量锁定。

确保从您锁定的线程以外的其他线程中调用dispatch_semaphore_signal()可以解决您的问题。

侧注: GCD和信号量在这里做的伎俩,但有一些更好的方法可以在异步操作完成后执行操作,而不是让线程被锁定。例如,委托回调。
没有足够的信息在这里建议任何确切的,但还有其他选择:)