Присвоение объектов переменной вне блока - PullRequest
8 голосов
/ 24 августа 2011

Сбой следующего кода, так как содержимое 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;
}

Как правильно / идиоматично сделать эту работу?

Ответы [ 3 ]

4 голосов
/ 24 августа 2011

Хорошо, я ушел и немного поиграл с 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 объекты.

ПРИМЕЧАНИЕ. Эта тема теперь является результатом Google номер 2 для автоматического выпуска enumerateObjectsUsingBlock:

Google 'enumerateObjectsUsingBlock + autorelease'

Первый результат подтверждает этот ответ. Спасибо всем.

1 голос
/ 24 августа 2011

Как вы упомянули, я подозреваю, что это происходит сбой при запуске пула автоматического выпуска, как это, вероятно, происходит в enumerateObjectsUsingBlock:. Это будет раздражать, если у вас есть переменная __block. Вместо этого вы можете использовать NSMutableString или просто сделать это, что в любом случае будет чище:

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

В качестве альтернативы, если вы используете ARC , компилятор должен устранить эту проблему для вас.

1 голос
/ 24 августа 2011

Хорошо, поэтому я не уверен на 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

...