Хранить блоки внутри словаря - PullRequest
14 голосов
/ 16 июня 2011

У меня есть собственный метод, который принимает блок в качестве аргумента.Я хочу отслеживать этот блок внутри 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. Блоки являются объектами objc
  2. Typo: 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);
    });
}

Ответы [ 4 ]

22 голосов
/ 16 июня 2011

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

Вам необходимо скопировать 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:.

12 голосов
/ 24 мая 2012

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

 void (^handlerCopy)(NSURLResponse*, NSData*, NSError*) = [handler copy];
 [dict setObject:handlerCopy forKey:@"foo"];
5 голосов
/ 24 мая 2012

«Typo: setObject должен быть установленValue»

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

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

[pendingRequests setObject:[[handler copy] autorelease] forKey:connection];
4 голосов
/ 03 апреля 2013

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

...