【问题标题】:Assigning objects to variable outside a block将对象分配给块外的变量
【发布时间】:2011-08-24 01:42:18
【问题描述】:

以下代码崩溃,因为sentence 的内容在最后一个块退出时消失了。

#import <Foundation/Foundation.h>    
int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    // simple block test - just iterate over some items and 
    // add them to a string
    NSArray *items = [NSArray arrayWithObjects:@"why ", @"must ", @"this ",nil];
    __block NSString *sentence = @"";   
    [items enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
    {
        sentence = [sentence stringByAppendingFormat:@"%@",obj];
    }];
    // crash!
    NSLog(@"Sentence is %@",sentence);
    [pool drain];
    return 0;
}

使这项工作的正确/惯用方式是什么?

【问题讨论】:

  • 哇,这很奇怪,我不知道为什么这不起作用。
  • 我见过人们在块的末尾做一个[[someVariable retain] autorelease] 来返回东西,但我不确定如果我怀疑自动释放池运行,为什么这会有什么不同。不过我不知道,这就是我问的原因,并且有各种各样的关于复制块和传递它们的文章,但我找不到像这样简单的东西。
  • 你得到的错误/异常是什么?
  • Program received signal: “EXC_BAD_ACCESS”. 它出错了,因为 sentence 一直是 release'd。我在下面概述了我认为正在发生的事情,这实际上与块无关。

标签: objective-c objective-c-blocks


【解决方案1】:

好的,我出去玩了一会儿 Xcode,这是正在发生的事情的模型,这似乎与我所看到的相符。

我上面使用的块没有做任何特别的事情,但enumerateObjectsUsingBlock 代码似乎有自己的NSAutoreleasePool,所以这似乎是导致dealloc 在对象alloc'ed 上被调用的原因,但在块内自动释放。

以下代码的行为与我在上面看到的相符:

#import <Foundation/Foundation.h> 
int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    // simple block test - just iterate over some items and 
    // add them to a string
    typedef void (^AccArrayBlock)(id obj, int idx, BOOL *stop);
    // items to 'process'
    NSArray *items = [NSArray arrayWithObjects:@"why ", @"must ", @"this ",nil];
    int idx = 0;
    BOOL doStop = NO;
    // make sentence mutable, so we can assign it inside block
    __block NSString *sentence = @"";
    // make a similar block to what we'd pass to enumerate...
    AccArrayBlock myBlock = ^(id obj, int idx, BOOL *stop)
    {
        // returns and assigns an autoreleased string object
        sentence = [sentence stringByAppendingFormat:@"(%d) %@  ",idx,obj];
    };
    // enumerate items and call block
    for (NSString *item in items) {
        // create a pool to clean up any autoreleased objects in loop
        // remove this line, and the sentence will be valid after loop
        NSAutoreleasePool *innerPool = [[NSAutoreleasePool alloc] init];
        myBlock(item, idx++, &doStop);
        // drain the pool, autorelease objects from block
        [innerPool drain];
        if (doStop) {
            break;
        }
    }
    // faults if we drained the pool
    // Program received signal:  “EXC_BAD_ACCESS”.
    NSLog(@"Sentence is %@",sentence);
    [pool drain];
    return 0;
}

如果我删除innerPool 对象,那么代码将按我最初的预期工作,并且大概NSRunLoop 池最终会清理各种NSString 对象。

注意:此线程现在是“enumerateObjectsUsingBlock autorelease”的第二个 Google 结果:

Google 'enumerateObjectsUsingBlock+autorelease'

第一个结果证实了这个答案。谢谢大家。

【讨论】:

    【解决方案2】:

    好的,所以我不能 100% 确定那里发生了什么,但与此同时,如果你改变它就会起作用

    NSArray *items = [NSArray arrayWithObjects:@"why ", @"must ", @"this ",nil];
    NSMutableString *sentence = [[NSMutableString alloc] init];   
    [items enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
    {
        [sentence appendFormat:@"%@",obj];
    }];
    
    NSLog(@"Sentence is %@",sentence); 
    
    [sentence release]; sentence = nil;
    

    感谢@nacho4d 更新

    【讨论】:

    • 我认为这种情况不需要__block修饰符。当然不要忘记[sentence release] :)
    • 谢谢,我在其他地方使用过类似的东西,但我正在努力研究如何在块中分配对象而不让它们消失。上面的代码只是我能想到的最简单的东西来演示我不理解的东西。当然,使用NSMutableString 编写这样的代码无论如何都会更有效,但我只是想要一些简单的演示代码。谢谢。
    • 你的答案可能在这里developer.apple.com/library/ios/#documentation/cocoa/Conceptual/… 但我太累了,无法理解它。也许你可以回答你自己的问题供大家参考
    • 是的,我自己都完蛋了。我想到的一件事是,在enumerateObjectsUsingBlock 的掩护下可能会发生很多事情。我将尝试使用自定义对象并确认 dealloc 被调用。烦人的是我对内存管理预块有很好的处理,但这确实给工作带来了麻烦。
    【解决方案3】:

    正如您所提到的,我怀疑这在自动释放池运行时会崩溃,就像在 enumerateObjectsUsingBlock: 中可能发生的那样。如果您有 __block 变量,这将很烦人。你可以使用 NSMutableString 代替,或者干脆这样做,反正更干净:

    for (id obj in items)
    {
         sentence = [sentence stringByAppendingFormat:@"%@",obj];
    }
    

    或者,如果您使用ARC,编译器应该会为您消除问题。

    【讨论】:

      猜你喜欢
      • 2011-12-19
      • 2012-07-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-22
      相关资源
      最近更新 更多