Хранить блоки внутри словаря

У меня есть собственный метод, который принимает блок в качестве аргумента. Я хочу отслеживать этот блок внутри NSDictionary. Как лучше всего добавить блок в словарь?

Я попробовал этот код, но после выполнения строки ниже (setObject...) словарь все еще пуст. Я предполагаю, что это потому, что блок не имеет типа NSObject. Но как правильно это сделать?

- (void)startSomething:(NSURLRequest*)request block:(void (^)(NSURLResponse*, NSData*, NSError*))handler {

    NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:self];

    [pendingRequests setObject:handler forKey:connection];
}

РЕДАКТИРОВАТЬ:

Не бери в голову. Я не знаю, о чем я думал. 3 балла:

  1. Блоки являются объектными объектами
  2. Опечатка: setObject должен быть setValue
  3. forKey — это строка, поэтому она должна быть [описание соединения] или что-то в этом роде.

Во всяком случае, теперь я исправил свою проблему следующим образом:

- (void)startSomething:(NSURLRequest*)request block:(void (^)(NSURLResponse*, NSData*, NSError*))handler {

    NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:self];
    [pendingRequests setValue:handler forKey:[connection description]];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {

        void (^handler)(NSURLResponse*, NSData*, NSError*);
        handler = [pendingRequests valueForKey:[connection description]];
        handler(nil, nil, nil);
    });
}

person vgr    schedule 15.06.2011    source источник
comment
Вы уверены, что это не вызывает ошибку? NSURLConnection не соответствует протоколу NSCopying и, следовательно, не может быть допустимым ключом для словаря.   -  person Deepak Danduprolu    schedule 16.06.2011
comment
Почему вы говорите, что setObject должно быть setValue? -setObject:forKey: — это канонический метод в NSMutableDictionary для хранения объектов в словаре, и ключ не обязательно должен быть строкой, если вы используете этот метод. Как сказал Дипак, он должен соответствовать протоколу NSCopying.   -  person    schedule 16.06.2011


Ответы (4)


Это все равно не сработает или, в лучшем случае, сработает только случайно.

Вам нужно скопировать handler перед тем, как засунуть его в словарь. Что-то типа:

void (^handlerCopy)(NSURLResponse*, NSData*, NSError*) = Block_copy(handler);
[dict setObject:handlerCopy forKey:@"foo"];
Block_release(handlerCopy); // dict will -retain/-release, this balances the copy.

И да, это должны быть setObject:forKey: и objectForKey:.

person bbum    schedule 15.06.2011
comment
Спасибо!! это следующая проблема, которую я имел, и всю прошлую ночь пытался понять это. Как только я увидел ваш пост сегодня утром, он решил мою проблему! Кстати, а зачем делать копию? Недостаточно сохраняется в исходном экземпляре, поскольку блоки являются объектными объектами. - person vgr; 16.06.2011
comment
Блоки начинаются в стеке и должны быть скопированы из стека, если они хотят жить за пределами стекового фрейма... Если бы это было сделано на retain, семантика сохранения изменилась бы довольно неприятным образом. - person bbum; 16.06.2011
comment
это Objective-C, поэтому вы можете использовать [... copy] и [... release] - person newacct; 24.05.2012
comment
Вы можете, да. Я использую Block_copy() и Block_release(), потому что они дают удобный способ поиска в любом месте, где я выполняю управление блочной памятью вручную (что, неудивительно, часто является источником ненужной хрупкости или признаком того, что я снова изобретая уже предложенное системой колесо). - person bbum; 24.05.2012

Если вы используете ARC, используйте -copy:

 void (^handlerCopy)(NSURLResponse*, NSData*, NSError*) = [handler copy];
 [dict setObject:handlerCopy forKey:@"foo"];
person Johan Kool    schedule 24.05.2012
comment
Блоки основаны на стеке, поэтому перед добавлением в словарь вам нужно [блокировать копию], если вы хотите, чтобы они работали должным образом. Дополнительные сведения: developer.apple.com/reference/foundation/nsmutabledictionary. / - person Lubbo; 08.11.2016

"Опечатка: setObject должен быть setValue"

НЕТ, вы всегда должны использовать setObject: вместо setValue:. setValue: предназначен для кодирования ключ-значение и по совпадению работает аналогично setObject: для словаря (даже тогда это не то же самое, например, когда ключ "@something"), в то время как setObject: является правильным методом для помещения вещей в словарь и который правильно принимает все типы в качестве ключей. (Кстати, я не уверен, что вы хотите использовать connection в качестве ключа, так как он скопирует его.)

Настоящая проблема заключается в том, что блоки необходимо копировать, прежде чем вы сохраните их в чем-то, что может длиться дольше, чем исходная область действия блока (это особая проблема для блоков, а не для других объектов), поскольку блоки (в отличие от других объектов) изначально находятся в стеке, поэтому их сохранение не предотвращает их освобождение. Помещение чего-либо в словарь обычно сохраняет это, но в данном случае этого недостаточно.

[pendingRequests setObject:[[handler copy] autorelease] forKey:connection];
person newacct    schedule 24.05.2012
comment
@Влад: просто удали autorelease - person newacct; 26.04.2016

На самом деле с ARC вы можете просто добавить блок в NSDictionary, как и любой другой объект. Вам не нужно делать ничего особенного, например, Block_copy или [block copy], и делать это было бы неправильно и приведет к утечке.

person Binks    schedule 03.04.2013
comment
Блоки основаны на стеке, поэтому перед добавлением в словарь вам нужно [блокировать копию], если вы хотите, чтобы они работали должным образом. Дополнительные сведения: developer.apple.com/reference/foundation/nsmutabledictionary. / - person Lubbo; 08.11.2016