2011-08-24 4 views
8

Następujący kod ulega awarii, ponieważ zawartość sentence odejdzie po zamknięciu ostatniego bloku.Przypisywanie obiektów do zmiennej poza blokiem

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

Jaki jest prawidłowy/idiomatyczny sposób, aby to zadziałało?

+0

Wow, to dziwne, nie jestem pewien, dlaczego to nie działa. – jtbandes

+0

Widziałem, jak ludzie wykonują autorelease '[[someVariable retain]] na końcu bloków, aby zwrócić rzeczy, ale nie jestem pewien, dlaczego to miałoby znaczenie, jeśli podejrzewam, że uruchamiana jest pula autorelease. Nie wiem jednak, dlatego pytam, i są wszelkiego rodzaju artykuły na temat kopiowania bloków i przekazywania ich, ale nic, co mogę znaleźć na czymś, co powinno być proste, jak to. –

+0

Jaki jest błąd/wyjątek, który otrzymujesz? – nacho4d

Odpowiedz

4

OK, odszedłem i grałem z Xcode przez chwilę, a oto model tego, co się dzieje, wydaje się pasować do tego, co widzę.

Blok użyłem powyżej nie robi nic szczególnego, ale enumerateObjectsUsingBlock kod wydaje się mieć swój własny NSAutoreleasePool, tak że wydaje się, co było przyczyną dealloc być wywołane na obiektach alloc'ed, ale autoreleased wewnątrz bloku.

Poniższy kod pasuje do zachowania tego, co widzę powyżej:

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

Jeśli usunąć obiekt innerPool, wówczas kod działa tak jak pierwotnie zakładano, a przypuszczalnie basen NSRunLoop końcu posprzątać różne obiekty NSString.

UWAGA: Ten wątek jest teraz numer 2 Google wynik dla 'autorelease enumerateObjectsUsingBlock':

Google 'enumerateObjectsUsingBlock+autorelease'

Pierwszy wynik potwierdza tę odpowiedź. Dziękuje wszystkim.

1

Ok, więc nie jestem w 100% pewien, co się tam dzieje, ale w tym czasie nie działa po zmianie

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; 

Zaktualizowane dzięki @ nacho4d

+1

W tej sprawie nie potrzebuję modyfikatora '__block'. I nie zapomnij o wydaniu '' of course ':) – nacho4d

+0

Ah tak dobrze złapać na obu –

+0

Dzięki, użyłem czegoś takiego gdzieś indziej, ale staram się wypracować jak zdobyć obiekty, przydzielone w bloku, poza blokiem, bez odchodzenia. Powyższy kod jest po prostu najprostszą rzeczą, jaką mogę wymyślić, aby zademonstrować coś, czego nie rozumiem.Oczywiście pisanie kodu takiego przy użyciu 'NSMutableString' jest bardziej wydajne, ale chciałem tylko trochę kodu demo. Dzięki. –

1

Jak wspomniano, podejrzewam to ulega awarii podczas działania puli autorelease, prawdopodobnie w wersji enumerateObjectsUsingBlock:. To będzie denerwujące do obejścia, jeśli masz zmienną __block. Można użyć NSMutableString zamiast, lub po prostu zrobić to, co jest czystsze i tak:

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

Alternatywnie, jeśli używasz ARC, kompilator powinno wyeliminować problem dla ciebie.