Блок освобождается в NSDictionary (ARC)

Я пытаюсь сохранить ссылку на блок, который был передан моему классу методом, чтобы вызвать его позже. Однако у меня возникли проблемы с сохранением ссылки на него.

Я подумал, что очевидным способом было бы добавить его в коллекцию ivar, каждая из которых должна содержать четкие ссылки на свое содержимое. Но когда я пытаюсь вытащить его обратно, это ноль.

Код довольно прост:

typedef void (^DataControllerCallback)(id rslt);

@interface DataController : NSObject {
    NSMutableArray* queue;
}
- (void) addBlock:(DataControllerCallback)callback;
- (void) functionToBeCalledLater;
@end

@implementation DataController

- (id) init {
    self = [super init];
    if (self != nil) {        
        queue = [NSMutableArray new];
    }
    return self;
}

- (void) addBlock:(DataControllerCallback)callback {
    NSDictionary* toAdd = [NSDictionary dictionaryWithObjectsAndKeys:
        [callback copy], @"callback",
        @"some other data", @"data", nil];
    [queue addObject:toAdd];
}

- (void) functionToBeCalledLater {
    NSDictionary* dict = [queue lastObject];
    NSLog(@"%@", [dict objectForKey:@"data"]; //works
    DataControllerCallback callback = [dict objectForKey:@"callback"]; //this is nil
    callback(@"an arguemnt"); //EXC_BAD_ACCESS
}

Что происходит?


Обновление: я пробовал это с [callback copy] и просто callback вставляя в словарь, ни один из них не работает.


Обновление 2: если я просто вставлю свой блок в NSMutableSet, пока я вызываю copy, все в порядке. Это прекрасно работает. Но если это в NSDictionary, это не так.

На самом деле я проверил это, поставив точку останова сразу после создания NSDict, и обратный вызов никогда не вставляется. В описании четко указано «1 пара ключ-значение», а не две.

В настоящее время я обхожу это специализированным классом, который действует как контейнер. Свойство callback объявлено как strong; Мне даже не нужно использовать copy.

Однако остается вопрос: почему это происходит? Почему NSDictionary не хранит блок? Это как-то связано с тем, что я нацелен на iOS 4.3, и поэтому ARC должен быть встроен как статическая библиотека?


Обновление 3: Дамы и господа: я идиот.

Представленный здесь код, очевидно, был упрощенной версией фактического кода; в частности, он оставлял некоторые пары ключ/значение вне словаря.

Если вы сохраняете значение в NSDictionary, используя [NSDictionary dictionaryWithObjectsAndKeys:], вам лучше быть черт возьми уверенным, что одно из этих значений не равно nil.

Один из них был.

ICYMI, это вызывало досрочное завершение списка аргументов. У меня был аргумент типа userInfo, который передавался в один из методов «добавить в очередь», и вы, конечно, могли передать «nil». Затем, когда я создал словарь, отказ от этого аргумента заставил конструктор думать, что я завершил список аргументов. @"callback" было последним значением в конструкторе словаря, и оно никогда не сохранялось.


person Morgan Harris    schedule 11.11.2011    source источник
comment
Я скопировал и вставил этот код с [копией обратного вызова], и все заработало нормально.   -  person Firoze Lafeer    schedule 14.11.2011
comment
functionToBeCalledLater возникает на другой итерации цикла выполнения, хотя я не думаю, что это имеет значение, но, возможно, это как-то связано с тем, где ARC размещает свои удержания и выпуски.   -  person Morgan Harris    schedule 14.11.2011
comment
Я также вызвал функциюToBeCalledLater на другой итерации цикла выполнения (сделал для нее небольшую кнопку и на самом деле нажал ее несколько раз)   -  person Firoze Lafeer    schedule 14.11.2011
comment
Можете ли вы собрать полностью компилируемый пример, в котором есть проблема, с которой вы столкнулись?   -  person Joshua Weinberg    schedule 14.11.2011
comment
И да, блок, который я проехал, не был статичным. Это сборка Xcode 4D199. Интересный.   -  person Firoze Lafeer    schedule 14.11.2011


Ответы (1)


person    schedule
comment
Всегда правильно возвращать блок на основе стека, и всегда ошибка возвращать блок на основе стека? Я подозреваю, что один из них должен сказать «куча». - person BJ Homer; 11.11.2011
comment
В «всегда правильно возвращать блок на основе стека (и всегда ошибка возвращать блок на основе стека)», разве это не должен быть статический блок в первом случае? - person ; 11.11.2011
comment
Ага -- ^стек^куча. Редактировать. Фиксированный. Спасибо. Чтобы было ясно - вы никогда не можете полагаться на то, что блок статичен или нет... так что нет, никогда нельзя возвращать блок на основе стека, и, таким образом, всегда требовалось возвращать блок на основе кучи и всегда означает автоматизацию в компиляторе. - person bbum; 11.11.2011
comment
За исключением сохранения, сделанного как часть инициализации переменной параметра __strong, я пропустил эту часть в документации. Есть ли причина, по которой он не помогает в этих случаях? - person Joshua Weinberg; 11.11.2011
comment
Отличный ответ. Удивительный, невероятно подробный ответ. Однако не работает. Обновил вопрос. - person Morgan Harris; 14.11.2011
comment
Эй, я отметил это как ответ/проголосовал, хотя это не решило мою первоначальную проблему, потому что это супер отличный ответ, и я не знал. Вы должны гордиться этим bbum. - person Morgan Harris; 14.11.2011
comment
С момента ответа прошло 3 года. Это все еще то же самое или компилятор гарантирует, что блоки стека копируются при передаче в массивы или словари? - person Ben Affleck; 05.08.2014