2011-08-24 96 views
8

由于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; 
} 

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

+0

哇,这是奇怪的,我不知道为什么不起作用。 – jtbandes

+0

我见过人们做了'[someVariable保留]自动释放]'在块的结束返回的东西,但我不知道为什么,应该使任何区别,如果我怀疑,一个自动释放池运行。我不知道,这就是为什么我问,而且有各种关于复制块的文章,并将它们传递给它们,但没有我能找到的应该很简单的东西,就像这样。 –

+0

你得到的错误/异常是什么? – nacho4d

回答

4

好了,我走了,和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自动释放' 2号谷歌的结果:

Google 'enumerateObjectsUsingBlock+autorelease'

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

1

好了,所以我不是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

+1

我这种情况下,我认为不需要'__block'修饰符。不要忘了当然:)'[释句]' – nacho4d

+0

啊是的好的捕获两个 –

+0

谢谢,我在其他地方使用过类似的东西,但我试图找出如何获得对象,分配在一个街区内,没有他们走开的街区之外。上面的代码只是我能想到的最简单的事情来展示我不明白的东西。当然,使用'NSMutableString'编写这样的代码无论如何都会更高效,但我只是想要一点简单的演示代码。谢谢。 –

1

至于你提到的作品,我怀疑这当autorelease池运行时崩溃,因为它可能在enumerateObjectsUsingBlock:中。如果你有一个__block变量,这将会很麻烦。你可以使用一个替代的NSMutableString,或简单地做到这一点,这是无论如何清洁:

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

另外,如果你使用ARC,编译器应该消除问题为您服务。

相关问题