目标c中的嵌套回调地狱
Posted
技术标签:
【中文标题】目标c中的嵌套回调地狱【英文标题】:nested callback hell in objective c 【发布时间】:2015-07-29 13:37:15 【问题描述】:我现在正在开发一个适用于 BLE、后端服务器和位置的应用程序。我面临一个我不知道如何解决的问题,这就是人们所说的“回调地狱”。 ios 中的整个 CoreBluetooth 框架都基于委托模式,在您可以使用 CBPeripheral 之前,必须至少进行 3 个回调:
DidConnectToPeripheral
DidDiscoverServices
DidDiscoverCharacteristics
但实际上可能还有更多,您对设备执行的每个操作都会作为对其中一个功能的回调返回。现在,当我想“租用”这个 ble 产品时,我必须连接到它,连接后向服务器发送请求并获取用户的当前位置,之后我必须在蓝牙设备中写入一个值并获得确认.这不会那么困难,但不幸的是,这些阶段中的每一个都是失败的,因此需要添加错误处理。更不用说实现超时了。
我确信我不是唯一一个解决此类问题的人,所以我环顾四周,发现 2 件事可能会有所帮助:
-
wwdc 2015 中的 Advanced NSOperations 演讲,但在尝试了 4 天后,似乎代码错误太多。
Promisekit 但我找不到封装 CoreBluetooth 的方法。
拥有更复杂应用程序的人如何处理这个问题?在 swift 或 objc 中。
一些有问题的示例代码:
-(void)startRentalSessionWithLock:(DORLock *)lock timeOut:(NSTimeInterval)timeout forSuccess:(void (^)(DORRentalSession * session))successBlock failure:(failureBlock_t)failureBlock
//we set the block to determine what happens
NSAssert(lock.peripheral, @"lock has to have peripheral to connect to");
if (!self.rentalSession)
self.rentalSession = [[DORRentalSession alloc] initWithLock:nil andSessionDict:@ active:NO];
self.rentalSession.lock = lock;
[self connectToLock:self.rentalSession.lock.peripheral timeOut:timeout completionBlock:^(CBPeripheral *peripheral, NSError *error)
self.BTConnectionCompleted = nil;
if (!error)
[[INTULocationManager sharedInstance] requestLocationWithDesiredAccuracy:INTULocationAccuracyHouse timeout:1 delayUntilAuthorized:YES block:^(CLLocation *currentLocation, INTULocationAccuracy achievedAccuracy, INTULocationStatus status)
if (status == INTULocationStatusSuccess || status == INTULocationStatusTimedOut)
[self startServerRentalForSessionLockWithUserLocation:currentLocation.coordinate forSuccess:^(DORRentalSession *session)
if (self.rentalSession.lock.peripheral && self.rentalSession.lock.peripheral.state == CBPeripheralStateConnected)
[self.rentalSession.lock.peripheral setNotifyValue:YES forCharacteristic:self.rentalSession.lock.charectaristics.sensorCharacteristic];
else
//shouldnt come here
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^
if (self.rentalSession.lock.peripheral.state == CBPeripheralStateConnected)
!self.rentalSession.lock.open ? [self sendUnlockBLECommandToSessionLock] : nil;
if (successBlock)
successBlock(session);
else
[self endCurrentRentalSessionWithLocation:self.rentalSession.lock.latLng andPositionAcc:@(1) Success:^(DORRentalSession *session)
if (failureBlock)
failureBlock([[NSError alloc] initWithDomain:DonkeyErrorDomain code:46 userInfo:@NSLocalizedDescriptionKey:@"Could't connect to lock"],200);
failure:^(NSError *error, NSInteger httpCode)
if (failureBlock)
failureBlock([[NSError alloc] initWithDomain:DonkeyErrorDomain code:45 userInfo:@NSLocalizedDescriptionKey:@"fatal error"],200);
];
);
failure:^(NSError *error, NSInteger httpCode)
if (failureBlock)
failureBlock(error,httpCode);
];
else
NSError *gpsError = [self donkeyGPSErrorWithINTULocationStatus:status];
if (failureBlock)
failureBlock(gpsError,200);
];
else
if (failureBlock)
failureBlock(error,200);
];
【问题讨论】:
【参考方案1】:要摆脱这种嵌套调用,您可以使用 GCD 组 + 串行执行队列:
dispatch_queue_t queue = ddispatch_queue_create("com.example.queue", NULL);
dispatch_group_t group = dispatch_group_create();
// Add a task to the group
dispatch_group_async(group, queue, ^
// Some asynchronous work
);
// Make dispatch_group_async and dispatch_group_sync calls here
// Callback to be executed when all scheduled tasks are completed.
dispatch_group_notify(serviceGroup,dispatch_get_main_queue(),^
// Do smth when everything has finished
);
// wait for all tasks to complete
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
另一种基于GCD组的解决方案描述here
【讨论】:
与 NSOperationQueue 相比,调度组有具体优势吗? 如果您想使用基于 NSOperation 的解决方案,您应该实现自定义 asynchronous 操作。使用调度组,您可以通过dispatch_group_enter
/dispatch_group_leave
调用来判断任务已开始/结束。以上是关于目标c中的嵌套回调地狱的主要内容,如果未能解决你的问题,请参考以下文章