Один из подходов заключается в отбрасывании шаблона делегата для другого шаблона обратного вызова: стиля продолжения. Идея состоит в том, чтобы ваш API принял блок продолжения в качестве параметра. Когда он делает свою работу, он просто вызывает блок продолжения. Вот пример процедуры API:
typedef void(^ContinuationBlock)(id result, NSError *error);
- (void) answerTheDefinitiveQuestion:(Towel *)h2g2Towel do:(ContinuationBlock*) continuation
{
if (h2g2Towel) {
double answer = 7 / h2g2Towel.length * (sqrt(h2g2Towel.area+2*h2g2Towel.area+1) - 1) * 6 / h2g2Towel.width;
continuation([NSNumber numberWithDouble:answer], nil);
} else {
NSError *err = [[[NSError alloc] initWithDomain:H2G2Domain code:kMissingTowel userInfo:nil] autorelease];
continuation(nil, err);
}
}
Теперь клиент может использовать его таким образом:
- (void) doSomething
{
[myServiceCaller answerTheDefinitiveQuestion:self.towel do:^(NSNumber * answer, NSError *error) {
if (!answer) {
NSLog("No answer available because %@, %@", error, error.userInfo);
} else {
NSLog("haha, Zaphod, your answer is %@, %@", answer);
}
}];
}
Примечание: этот пример является синхронным, но он прекрасно работает в асинхронном контексте. Например, вы можете заключить в скобки реализацию answerTheDefinitiveQuestion:do:
при вызове dispatch_async
.
Действительно, этот подход очень хорошо подходит для полностью асинхронного API. Единственное, что нужно помнить, это то, что если вы сохраняете блок продолжения для последующего вызова, вы должны сохранить копию, потому что блок, который вы получите, основан на стеке и исчезнет, когда ваша функция вернется. Просто сделайте:
self.myClientContinuation = [continuation copy];
PS: вся идея синглтона - зло. По крайней мере, в вашем дизайне, похоже, что ServiceCaller не имеет смысла быть единственным (в отличие от ServiceConnector).