Сохранение делегата до его использования - PullRequest
2 голосов
/ 11 января 2012

Итак, я пишу этот 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.

Итак, мой вопрос: есть ли лучший способ предотвратить освобождение делегатов, а затем освободить их после вызова загрузчиком?

1 Ответ

0 голосов
/ 28 июня 2013

Я недавно задал аналогичный вопрос и узнал о «связанных объектах».Похоже, что это может решить вашу проблему.

@ CRD дает хорошее объяснение в своем ответе - см. https://stackoverflow.com/a/17351118/423565

...