2011-08-24 11 views
8

Il seguente codice si blocca, poiché il contenuto di sentence scompare quando il blocco finale viene chiuso.Assegnazione di oggetti a variabili esterne a un blocco

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

Qual è il modo corretto/idiomatico per farlo funzionare?

+0

Wow, è bizzarro, non sono sicuro del perché non funzioni. – jtbandes

+0

Ho visto persone eseguire un'autorelease '[[someVariable retain]] alla fine dei blocchi per restituire le cose, ma non sono sicuro del motivo per cui ciò dovrebbe fare la differenza se, come sospetto, venga eseguito un pool di autorelease. Non so però, è per questo che lo sto chiedendo, e ci sono tutti i tipi di articoli su come copiare i blocchi e passarli in giro, ma nulla che io possa trovare su qualcosa che dovrebbe essere semplice, come questo. –

+0

Qual è l'errore/l'eccezione che ottieni? – nacho4d

risposta

4

Ok, sono andato via e ho giocato con Xcode per un po ', ed ecco un modello di cosa sta succedendo, che sembra corrispondere a quello che sto vedendo.

Il blocco che ho usato in precedenza non sta facendo nulla di speciale, ma il codice di enumerateObjectsUsingBlock sembra avere una propria NSAutoreleasePool, in modo che sembra essere quello che stava causando dealloc di essere chiamato su oggetti alloc'ed, ma autoreleased all'interno del blocco.

Il seguente codice corrisponde a un comportamento quello che sto vedendo qui sopra:

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

Se rimuovo l'oggetto innerPool, quindi il codice funziona come mi aspettavo in origine, e presumibilmente la piscina NSRunLoop finirà per ripulire il vari oggetti NSString.

NOTA: Questo thread è ora il risultato numero 2 Google per 'enumerateObjectsUsingBlock autorelease':

Google 'enumerateObjectsUsingBlock+autorelease'

Il primo risultato conferma questa risposta. Ringrazia tutti.

1

Ok quindi non sono sicuro al 100% quello che sta succedendo lì, ma nel frattempo funziona se 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; 

aggiornati grazie a @ nacho4d

+1

In questo caso penso che non sia necessario il modificatore '__block'. E non dimenticare di '[rilascio frase]' ovviamente :) – nacho4d

+0

Ah sì buona presa su entrambi –

+0

Grazie, ho usato qualcosa del genere da qualche altra parte, ma sto cercando di capire come ottenere oggetti, assegnati in un isolato, fuori da un isolato senza che se ne vadano. Il codice sopra è solo la cosa più semplice che potessi pensare per dimostrare qualcosa che non capisco.Naturalmente scrivere codice come questo usando 'NSMutableString' è comunque più efficiente, ma volevo solo un po 'di codice demo. Grazie. –

1

Come lei ha ricordato, ho il sospetto che questo si arresta in modo anomalo quando viene eseguito il pool autorelease, come probabilmente avviene in enumerateObjectsUsingBlock:. Ciò sarà noioso per aggirare se si dispone di una variabile __block. È possibile usare un NSMutableString, invece, o semplicemente fare questo, che è più pulito in ogni caso:

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

In alternativa, se si utilizza ARC, il compilatore dovrebbe eliminare il problema per voi.

Problemi correlati