【问题标题】:Keep blocks inside a dictionary将块保存在字典中
【发布时间】:2011-06-15 21:31:58
【问题描述】:

我有自己的方法,将块作为参数。我想跟踪 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. 错字: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);
    });
}

【问题讨论】:

  • 你确定这不会引发错误吗? NSURLConnection 不符合 NSCopying 协议,因此不能成为字典的有效键。
  • 为什么说setObject应该是setValue? -setObject:forKey: 是NSMutableDictionary 中用于将对象存储到字典中的规范方法,如果使用此方法,键不需要是字符串。正如 Deepak 所说,它需要符合 NSCopying 协议。

标签: ios nsdictionary objective-c-blocks grand-central-dispatch


【解决方案1】:

这仍然行不通,或者充其量只是巧合。

您需要先复制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:。

【讨论】:

  • 谢谢!!那是我遇到的下一个问题,并且昨晚整晚都在试图解决这个问题。今天早上看到你的帖子后,它解决了我的问题!顺便问一下,制作副本的原因是什么?由于块是 objc 对象,因此在原始实例上保留不够。
  • 块从堆栈开始,如果要在堆栈框架之外生存,则需要从堆栈中复制...在retain上完成它会改变保留的语义相当不愉快的时尚。
  • 这是Objective-C,所以你可以使用[... copy]和[... release]
  • 你可以,是的。我使用 Block_copy() 和 Block_release() 因为它提供了一个很好的钩子来搜索我手动进行块内存管理的任何地方(毫不奇怪,这通常是不必要的脆弱性的来源或表明我正在重新-发明一个系统已经提供的轮子)。
【解决方案2】:

如果您使用 ARC,请使用 -copy:

 void (^handlerCopy)(NSURLResponse*, NSData*, NSError*) = [handler copy];
 [dict setObject:handlerCopy forKey:@"foo"];

【讨论】:

【解决方案3】:

“错字:setObject 应该是 setValue”

不,您应该始终使用setObject: 而不是setValue:。 setValue: 用于键值编码,巧合地与setObject: 用于字典类似(即使那样,它也不相同,例如当键为"@something" 时),而setObject: 是放置东西的正确方法在字典中,并且正确地将所有类型作为键。 (顺便说一句,我不确定您是否要使用 connection 作为密钥,因为它会复制它。)

真正的问题是,在将块存储在可能比块的原始范围更长的地方之前需要复制块(这是块的特殊问题,而不是其他对象),因为块(不像其他对象)最初在堆栈上,因此保留它并不会阻止它被释放。将某些内容放入字典中通常会保留它,但在这种情况下这还不够。

[pendingRequests setObject:[[handler copy] autorelease] forKey:connection];

【讨论】:

  • @Vlad:只需删除 autorelease
【解决方案4】:

实际上,使用 ARC,您可以像使用任何其他对象一样简单地将块添加到 NSDictionary。你不需要做任何特别的事情,比如 Block_copy 或 [block copy],这样做是错误的,会导致泄漏。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-18
  • 2021-08-28
  • 1970-01-01
  • 2016-11-12
  • 2017-07-14
  • 1970-01-01
相关资源
最近更新 更多