我正在使用CoreBluetooth
使用Bluetooth Low Energy与外围设备连接和交换数据。要连接到我的外围设备,我使用以下方法(为清楚起见,manager
是CBCentralManager
类的实例)。
- (void)connectPeripheral:(CBPeripheral *)peripheral {
// Connects with the peripheral
[manager connectPeripheral:peripheral options:nil];
}
现在我希望使用Grand Central Dispatch,块和信号量编写此方法的异步版本。我想有一个在特定时间间隔内超时的版本。首先我定义了这个方法:
void dispatchAsyncWithCompletionAndTimeout(dispatch_queue_t queue, int64_t timeoutInNanoseconds,
dispatch_block_t block, dispatch_block_t completionBlock,
dispatch_block_t timeoutBlock) {
NSCParameterAssert(queue);
NSCParameterAssert(timeoutInNanoseconds >= 0);
NSCParameterAssert(block);
dispatch_async(queue, ^{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_time_t timeoutTime = dispatch_time(DISPATCH_TIME_NOW, timeoutInNanoseconds);
dispatch_async(queue, ^{
long timedOut = dispatch_semaphore_wait(semaphore, timeoutTime);
if (timedOut) {
if (timeoutBlock) {
timeoutBlock();
}
} else if (completionBlock) {
completionBlock();
}
});
block();
dispatch_semaphore_signal(semaphore);
});
}
此函数基本上包含三个块:第一个是动作块(例如connect),第二个和第三个是处理程序块,无论动作代码是在超时到期时间内还是在超时到期后执行,都应该调用它。 / p>
我接下来要做的是将connectPeripheral:
方法转换为异步方法,方法是将其包含在以下方法中:
- (void)connectPeripheralAnsync:(CBPeripheral *)peripheral withinTimeout:(NSInteger)timeout {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatchAsyncWithCompletionAndTimeout(queue, (int64_t)timeout * NSEC_PER_SEC, ^{
[self connectPeripheral:peripheral];
}, ^{
NSLog(@"Peripheral discovered");
}, ^{
NSLog(@"Time runned out");
});
}
不幸的是,我的第三个块永远不会被调用,即使在超时到期之前管理器也找不到外围设备。我在哪里失败?
答案 0 :(得分:1)
看起来GCD没有问题
我得到你的代码并重新制作一点点来检查超时的东西很简单,所以改变secsForTask和secsForTimeout我可以查看所谓的:
unsigned int secsForTask = 3;
unsigned int secsForTimeout = 2;
dispatch_queue_t queue = dispatch_queue_create("com.test.111", DISPATCH_QUEUE_CONCURRENT);
int64_t timeoutInNanoseconds = secsForTimeout * NSEC_PER_SEC;
dispatch_async(queue, ^{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_time_t timeoutTime = dispatch_time(DISPATCH_TIME_NOW, timeoutInNanoseconds);
dispatch_block_t timeoutBlock = ^{
NSLog(@"timeout");
};
dispatch_block_t completionBlock = ^{
NSLog(@"completion");
};
dispatch_block_t block = ^{
NSLog(@"block start");
sleep(secsForTask);
NSLog(@"block end");
};
dispatch_async(queue, ^{
long timedOut = dispatch_semaphore_wait(semaphore, timeoutTime);
if (timedOut) {
if (timeoutBlock) {
timeoutBlock();
}
} else if (completionBlock) {
completionBlock();
}
});
block();
dispatch_semaphore_signal(semaphore);
});
on secsForTask = 3 and secsForTimeout = 2
阻止开始
超时
阻止结束
on secsForTask = 1 and secsForTimeout = 2
阻止开始
阻止结束 完成
可能你必须检查你的超时是否一切正常,所以你没有传入方法connectPeripheralAnsync:withinTimeout:timeout in nanosecs。
答案 1 :(得分:0)
-[CBCentralManager connectPeripheral:options:]方法是一种非阻塞异步方法。 因此,此方法调用总是立即通过下一行。
在您的dispatchAsyncWithCompletionAndTimeout(...)方法的情况下。 执行包含“-[CBCentralManager connectPeripheral:options:]”的块。 然后dispatch_semaphore_signal(semaphore);在不阻塞线程的情况下执行。 因此,您总是无法超时。
-[CBCentralManager connectPeripheral:options:]调用将响应 -[[CBCentralDelegate centralManager:didConnect:]连接成功时。
因此,您应该使用不同的方法。 dispatch_semaphore_signal(semaphore);应该调用-[CBCentralDelegate centralManager:didConnect:]。