Keep blocks inside a dictionary
I have my own method that takes a block as an a开发者_运维知识库rgument. I want to keep track of that block inside an NSDictionary. What is the best way to add the block to the dictionary?
I tried this code but after executing the line below (setObject...) the dictionary is still empty. I presume that is because the block is not of type NSObject. But what is the right way to do this?
- (void)startSomething:(NSURLRequest*)request block:(void (^)(NSURLResponse*, NSData*, NSError*))handler {
NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:self];
[pendingRequests setObject:handler forKey:connection];
}
EDIT:
Never mind. I don't know what I was thinking. 3 points:
- Blocks are objc objects
- Typo: setObject should be setValue
- forKey is a string so it should be [connection description] or something like that
Anyway I fixed my problem now like this:
- (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);
});
}
That still isn't going to work or, at best, will only work coincidentally.
You need to copy the handler
before shoving it in the dictionary. Something like:
void (^handlerCopy)(NSURLResponse*, NSData*, NSError*) = Block_copy(handler);
[dict setObject:handlerCopy forKey:@"foo"];
Block_release(handlerCopy); // dict will -retain/-release, this balances the copy.
And, yes, it should be setObject:forKey:
and objectForKey:
.
If you are using ARC, use -copy
:
void (^handlerCopy)(NSURLResponse*, NSData*, NSError*) = [handler copy];
[dict setObject:handlerCopy forKey:@"foo"];
"Typo: setObject should be setValue"
NO, you should always use setObject:
instead of setValue:
. setValue:
is for key-value coding and coincidentally works similar to setObject:
for a dictionary (even then, it's not the same, e.g. when the key is "@something"
), while setObject:
is the correct method for putting things in a dictionary, and which correctly takes all types as keys. (By the way I'm not sure you want to use connection
as a key since it will copy it.)
The real problem is that blocks need to be copied before you store them away in something that might last longer than the original scope of the block (this is a special issue for blocks and not for other objects), since blocks (unlike other objects) are initially on the stack, and thus retaining it does not prevent it from becoming deallocated. Putting something in a dictionary normally retains it, but that is not enough in this case.
[pendingRequests setObject:[[handler copy] autorelease] forKey:connection];
Actually with ARC you can simply add the block to the NSDictionary as you would with any other object. You do not need to do anything special like Block_copy or [block copy], and doing that would be wrong and will cause a leak.
精彩评论