Итак, я пишу этот API, который выглядит примерно так:
@implementation TheApi
- (ObjectLoaderCallbackDelegate *)createLoaderDelegateForCallback:(ObjectLoaderCallback)callback
{
ObjectLoaderCallbackDelegate *loaderDelegate = [[ObjectLoaderCallbackDelegate alloc] init];
loaderDelegate.didLoadObject = ^(id object) {
callback(object);
};
return loaderDelegate;
}
- (void)loadString:(ObjectLoaderCallback)callback
{
ObjectLoaderCallbackDelegate *callbackDelegate = [self createLoaderDelegateForCallback:callback];
ObjectLoader *loader = [[ObjectLoader alloc] init];
[loader load:@"string" delegate:callbackDelegate];
}
- (void)loadNumber:(ObjectLoaderCallback)callback
{
ObjectLoaderCallbackDelegate *callbackDelegate = [self createLoaderDelegateForCallback:callback];
ObjectLoader *loader = [[ObjectLoader alloc] init];
[loader load:@"number" delegate:callbackDelegate];
}
@end
Когда я вызываю [загрузчик загрузки: делегат:], он не сохраняет сильную ссылку на делегата (что имеет смысл). Но поскольку загрузчик выполняет асинхронные операции перед вызовом делегата, делегат освобождается еще до того, как его вызывают, что приводит к аварийному завершению программы. Вот мое решение (которое кажется немного грязным):
@interface TheApi : NSObject
- (void)loadString:(ObjectLoaderCallback)callback;
- (void)loadNumber:(ObjectLoaderCallback)callback;
- (void)runCalls;
@end
@implementation TheApi
{
NSMutableDictionary *loaderDelegates;
NSMutableSet *callbacksCompleted;
}
- (TheApi *) init
{
self = [super init];
if (self != nil) {
loaderDelegates = [[NSMutableDictionary alloc] init];
callbacksCompleted = [[NSMutableSet alloc] init];
}
return self;
}
- (void)runCalls
{
[loader runLoop];
}
- (ObjectLoaderCallbackDelegate *)createLoaderDelegateForCallback:(ObjectLoaderCallback)callback
{
NSNumber *delegateRefKey = [NSNumber numberWithUnsignedInt:arc4random()];
ObjectLoaderCallbackDelegate *loaderDelegate = [[ObjectLoaderCallbackDelegate alloc] init];
loaderDelegate.didLoadObject = ^(id object) {
callback(object);
[callbacksCompleted addObject:delegateRefKey];
};
[loaderDelegates setObject:loaderDelegate forKey:delegateRefKey];
return loaderDelegate;
}
- (void)removeCompletedDelegates
{
// So we can remove items from callbacksCompleted in the loop...
NSMutableSet *callbacksCompletedIterSet = [callbacksCompleted copy];
// Remove old delegates for calls already completed which are stored
for (id key in callbacksCompletedIterSet) {
[loaderDelegates removeObjectForKey:key];
[callbacksCompleted removeObject:key];
}
}
- (void)loadString:(ObjectLoaderCallback)callback
{
[self removeCompletedDelegates];
ObjectLoaderCallbackDelegate *callbackDelegate = [self createLoaderDelegateForCallback:callback];
ObjectLoader *loader = [[ObjectLoader alloc] init];
[loader load:@"string" delegate:callbackDelegate];
}
- (void)loadNumber:(ObjectLoaderCallback)callback
{
[self removeCompletedDelegates];
ObjectLoaderCallbackDelegate *callbackDelegate = [self createLoaderDelegateForCallback:callback];
ObjectLoader *loader = [[ObjectLoader alloc] init];
[loader load:@"number" delegate:callbackDelegate];
}
@end
Итак, здесь я храню ссылку на делегатов в словаре уровня экземпляра, где их ключ уникален для каждого вызова метода API.
Итак, мой вопрос: есть ли лучший способ предотвратить освобождение делегатов, а затем освободить их после вызова загрузчиком?