【问题标题】:Obj-C __block variable retain behaviorObj-C __block 变量保留行为
【发布时间】:2011-06-15 19:11:08
【问题描述】:

我在尝试从修改它的块外部访问 __block(块可变)变量时遇到了一个奇怪的问题。这是一个非常简单的示例,我使用它只是为了更好地理解块,但目前我有一个使用这种方法的控制器,它创建一个包含NSDictionary 内容的字符串,它使用NSDictionary's enumerateKeysAndObjectsUsingBlock:

- (NSString*) contentsOfDictionary:(NSDictionary*)dictionary
{
    __block NSString *content = @"";

    [dictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop){
        NSString* contentToAppend = [NSString stringWithFormat:@"Object:%@ for key:%@\n", obj, key];
        content = [content stringByAppendingString:contentToAppend];
        NSLog(@"Content in block:\n%@", content);
    }];

    NSLog(@"Content out of block:\n%@", content);

    return content;
}

当我使用包含内容的字典运行此方法时:

Value    Key
"Queen"  "card"
"Hearts" "suit"
"10"     "value"

content 变量在块内被正确修改,每次迭代我都会得到这个输出:

...块中的内容:

Object:Queen for key:card

...块中的内容:

Object:Queen for key:card
Object:Hearts for key:suit

...块中的内容:

Object:Queen for key:card
Object:Hearts for key:suit
Object:10 for key:value

一旦代码跳出代码块,访问content 字符串会抛出EXC_BAD_ACCESS,并且在一次运行时它似乎打印了一些垃圾内存(无法重现)...

是什么导致这个变量被提前释放?我的印象是,给它一个 __block 定义意味着它在块中使用时被保留,并在块退出时释放 - 但是变量被保留并自动释放以开始,因为它是一个字符串文字所以我希望在此方法最早退出之前不会释放它。

【问题讨论】:

    标签: objective-c memory-management objective-c-blocks


    【解决方案1】:

    这是你的问题:

    content = [content stringByAppendingString:contentToAppend];
    

    -stringByAppendingString: 返回一个新的、自动释放的对象。该对象的地址存储在content 中。每一次都经过这个(隐式)循环——也就是说,每次调用所提供的块——都在创建一个全新的对象,然后将该新对象的地址分配给content。这些对象都没有超过其包含的自动释放池。

    您应该做的是使用NSMutableString 并将contentToAppend 直接附加到可变字符串。例如:

    - (NSString*) contentsOfDictionary:(NSDictionary*)dictionary
    {
        NSMutableString *content = [NSMutableString string];
        [dictionary enumerateKeysAndObjectsUsingBlock:
        ^(id key, id obj, BOOL *stop){
            NSString* contentToAppend = [NSString stringWithFormat:
                @"Object:%@ for key:%@\n", obj, key];
            [content appendString:contentToAppend];
    
            NSLog(@"Content in block:\n%@", content);
        }];
    
        NSLog(@"Content out of block:\n%@", content);
        return content;
    }
    

    请注意,不再需要 __block,因为您不会在块内的任何位置分配 content

    【讨论】:

      【解决方案2】:

      在内部,-enumerateKeysAndObjectsUsingBlock: 正在使用自动释放池。 __block 范围内的对象在块的生命周期结束后不会保留,因此您最终会在块的范围内创建一个对象,然后在字典的自动释放池耗尽时释放该对象,这一切都发生在您尝试打印之前content 的值。

      【讨论】:

      • 如何防止字典的自动释放池被耗尽?我的所作所为是否存在根本性的错误?
      • 您不能覆盖池,但可以在枚举中保留对象(确保在循环时不要泄漏对象!),然后在循环时自动释放或释放它重新完成它。
      • 我明白了,隐式自动释放的 stringByAppendingString 可以保留并手动释放以避免该问题,但我猜 MutableString 使这更容易。我感觉这不会是我最后一个与内存管理相关的块问题:(
      • donalbain,这不是真正的块问题,autorelease 池位于-enumerateKeysAndObjectsUsingBlock 内。您始终可以自己编写方法,就像我所做的那样:here,尽管在这种确切情况下最好的办法是使用NSMutableString
      猜你喜欢
      • 2023-03-28
      • 2012-02-11
      • 2015-11-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多