2011-08-24 32 views
8

El siguiente código falla, ya que los contenidos de sentence desaparecen cuando finaliza el bloque final.Asignación de objetos a la variable fuera de un bloque

#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; 
} 

¿Cuál es la forma correcta/idiomática de hacerlo funcionar?

+0

Guau, eso es extraño, no estoy seguro de por qué eso no funciona. – jtbandes

+0

He visto a gente hacer una liberación automática '[[someVariable retain]] al final de bloques para devolver cosas, pero no estoy seguro de por qué eso debería marcar la diferencia si, como sospecho, se ejecuta un grupo de autorrelease. No sé, sin embargo, es por eso que pregunto, y hay todo tipo de artículos sobre cómo copiar bloques y pasarlos, pero no hay nada que pueda encontrar en algo que debería ser simple, como este. –

+0

¿Cuál es el error/excepción que obtienes? – nacho4d

Respuesta

4

Ok, me fui y jugué un poco con Xcode, y aquí hay un modelo de lo que está sucediendo, que parece coincidir con lo que estoy viendo.

El bloque utilicé anterior no está haciendo nada especial, pero el código enumerateObjectsUsingBlock parece tener su propia NSAutoreleasePool, por lo que parece ser lo que estaba causando dealloc para ser llamado en objetos alloc'ed, pero autoreleased dentro del bloque.

El siguiente código coincide con el comportamiento en lo que estoy viendo arriba:

#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; 
} 

Si quito el objeto innerPool, a continuación, el código funciona como esperaba originalmente, y, presumiblemente, la piscina NSRunLoop muy probable que limpiar el varios objetos NSString.

NOTA: Este hilo es ahora el número 2 Google resultado para 'enumerateObjectsUsingBlock autorelease':

Google 'enumerateObjectsUsingBlock+autorelease'

El primer resultado confirma esta respuesta. Gracias a todos.

1

Ok, así que no estoy 100% seguro de lo que está pasando allí, pero en la media hora que funciona si cambia

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; 

gracias Actualizado a @ nacho4d

+1

En este caso, creo que no es necesario el modificador '__block'. Y no te olvides de '[sentencia liberación]' por supuesto :) – nacho4d

+0

Ah sí buena captura en ambos –

+0

Gracias, he usado algo así en otro lugar, pero estoy tratando de averiguar cómo conseguir objetos, asignados en un bloque, fuera de una cuadra sin que ellos se vayan. El código anterior es lo más simple que se me ocurrió para demostrar algo que no entiendo.Por supuesto, escribir código como este usando 'NSMutableString' es más eficiente de todos modos, pero solo quería un simple código de demostración. Gracias. –

1

Como usted ha mencionado, sospecho que esto se bloquea cuando se ejecuta el grupo de autorrelease, como probablemente lo haga en enumerateObjectsUsingBlock:. Esto será molesto si obtienes una variable __block. Se podría utilizar un NSMutableString lugar, o simplemente hacer esto, lo que es más limpio de todos modos:

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

Alternativamente, si se utiliza ARC, el compilador debe eliminar el problema para usted.

Cuestiones relacionadas