14

Próbuję zachować odniesienie do bloku, który został przekazany do mojej klasy za pomocą metody, aby zadzwonić w późniejszym czasie. Mam jednak problem z utrzymaniem odniesienia do niego.Blok zostaje zwolniony, podczas gdy w NSDictionary (ARC)

Oczywistym sposobem, pomyślałem, było dodanie go do kolekcji ivar, z których wszystkie mają utrzymywać silne odniesienia do ich zawartości. Ale kiedy próbuję ją wyciągnąć, jest zero.

Kod jest dość prosta:

typedef void (^DataControllerCallback)(id rslt); 

@interface DataController : NSObject { 
    NSMutableArray* queue; 
} 
- (void) addBlock:(DataControllerCallback)callback; 
- (void) functionToBeCalledLater; 
@end 

@implementation DataController 

- (id) init { 
    self = [super init]; 
    if (self != nil) {   
     queue = [NSMutableArray new]; 
    } 
    return self; 
} 

- (void) addBlock:(DataControllerCallback)callback { 
    NSDictionary* toAdd = [NSDictionary dictionaryWithObjectsAndKeys: 
     [callback copy], @"callback", 
     @"some other data", @"data", nil]; 
    [queue addObject:toAdd]; 
} 

- (void) functionToBeCalledLater { 
    NSDictionary* dict = [queue lastObject]; 
    NSLog(@"%@", [dict objectForKey:@"data"]; //works 
    DataControllerCallback callback = [dict objectForKey:@"callback"]; //this is nil 
    callback(@"an arguemnt"); //EXC_BAD_ACCESS 
} 

Co się dzieje?


Aktualizacja: Próbowałem go [callback copy] i tylko callback włożeniem do słownika, ani prace.


Aktualizacja 2: Gdybym tylko trzymać mój blok się na NSMutableSet, tak długo, jak ja to nazywam copy, jestem w porządku. Działa świetnie. Ale jeśli jest w NSDictionary, nie ma.

Rzeczywiście przetestowałem to poprzez umieszczenie punktu przerwania zaraz po utworzeniu NSDict i wywołanie zwrotne nigdy nie zostanie wstawione. W opisie wyraźnie widać "1 parę klucz-wartość", a nie dwie.

W tej chwili używam specjalistycznej klasy, która działa jak pojemnik. Właściwość callback jest zadeklarowana jako strong; Nie muszę nawet używać copy.

Pytanie pozostaje jednak następujące: dlaczego tak się dzieje? Dlaczego NSDictionary nie będzie przechowywać bloku? Czy ma to coś wspólnego z tym, że celuję w system operacyjny iOS 4.3, a zatem ARC musi być wbudowany jako biblioteka statyczna?


Aktualizacja 3: Panie i panowie: Jestem idiotą.

Przedstawiony tutaj kod był oczywiście uproszczoną wersją rzeczywistego kodu; w szczególności, pozostawił niektóre pary klucz/wartość ze słownika.

Jeśli przechowywania wartości w NSDictionary za pomocą [NSDictionary dictionaryWithObjectsAndKeys:], to lepiej być cholernie pewien jedna z tych wartości nie jest nil.

Jednym z nich był.

ICYMI, było przyczyną wcześniejszego zakończenia listy argumentów. Miałem argument typu userInfo przekazywany do jednej z metod "dodaj do kolejki" i możesz oczywiście przekazać "zero". Następnie, kiedy skonstruowałem słownik, zarzucenie tego argumentu spowodowało, że konstruktor pomyślał, że zakończyłem listę argumentów. @"callback" była ostatnią wartością w konstruktorze słownika i nigdy nie była przechowywana.

+0

Skopiowałem i wkleiłem ten kod za pomocą [kopii wywołania zwrotnego] i wszystko działało dobrze. –

+0

Funkcja functionToBeCalledLater występuje w innej iteracji pętli uruchamiania, chociaż nie sądziłem, że ma to znaczenie, ale może ma to coś wspólnego z tym, gdzie ARC umieszcza swoje zachowania i wydania. –

+0

Nazwałem functionToBeCalledLater na innej iteracji runloopa również (zrobiłem dla niego mały przycisk i trafiłem to kilka razy). –

Odpowiedz

31

W przeciwieństwie do popularnej koncepcji błędnej, ARC nie usuwa automatycznie stacku bloków przekazanych jako argumenty metodom. Automatycznie wyłącza się tylko przy zwrocie bloku z metody/funkcji.

tj. to....

[dict setObject: ^{;} forKey: @"boom"]; 

... padnie jeśli dict przeżyje poza zakresem i spróbujesz użyć bloku (właściwie, to nie będzie w tym przypadku, ponieważ jest to statyczny blok, ale to szczegół kompilator to ty nie można polegać na).

To documented here:

Jak bloki pracują w SM?

Bloki "po prostu działają", gdy przechodzisz bloki w górę stosu w trybie ARC, na przykład jak w przypadku zwrotu. Nie musisz już wywoływać funkcji Block Copy. Musisz nadal trzeba użyć [^ {} copy] podczas przekazywania "w dół" stosu do arrayWithObjects: i innych metod, które zachowują.

Zachowanie wartość zysk może być zautomatyzowane, ponieważ jest zawsze poprawne powrócić blok oparty sterty (i zawsze błąd powrotu blok oparty stosu). W przypadku bloku jako argumentu niemożliwe jest zautomatyzowanie zachowania w sposób, który byłby zarówno bardzo wydajny, jak i zawsze poprawny.

Analizator prawdopodobnie powinien był ostrzec o tym użyciu. Jeśli nie, zgłoś błąd.

(I derped się stos kiedy oznaczało sterty przepraszam..)


Kompilator nie zautomatyzować bloki-as-parametry z kilku powodów:

  • niepotrzebne kopiowanie bloku do sterty może być znaczącą karą za wydajność
  • wielokrotne kopie bloku mogą pomnożyć wydajność kar znacznie.

tj .:

doSomethingSynchronous(aBlock); 
doSomethingSynchronous(aBlock); 
doSomethingSynchronous(aBlock); 
doSomethingSynchronous(aBlock); 

Gdyby tak było sugerować cztery Block_copy() operacje i aBlock zawierały znaczne ilości przechwyconego państwo, że byłoby to ogromny potencjał hit.

• Jest tak wiele godzin w ciągu dnia, a automatyzacja obsługi parametrów jest bardzo duża w przypadku nieoczywistych przypadków. Gdyby to było obsługiwane automatycznie w przyszłości, można by to zrobić bez łamania istniejącego kodu, a więc być może nastąpi to w przyszłości.

tj. Kompilator może generować:

aBlock = [aBlock copy]; 
doSomethingSynchronous(aBlock); 
doSomethingSynchronous(aBlock); 
doSomethingSynchronous(aBlock); 
doSomethingSynchronous(aBlock); 
[aBlock release]; 

Nie tylko byłoby to rozwiązać problem z bloku-as-param, ale to również produkują tylko jedną kopię bloku we wszystkich możliwych zastosowań.


pytanie nadal stoi, ale: dlaczego tak się dzieje?Dlaczego serwer NSDictionary w wersji nie będzie przechowywać bloku? Czy ma to coś wspólnego z faktem, że wybieram system iOS 4.3, a zatem ARC musi być wbudowany jako biblioteka statyczna ?

Coś dziwnego się dzieje. Przypadkowo użyłem bloków wartości w aplikacji opartej na ARC w zeszłym tygodniu i działa dobrze.

Czy masz minimalny przykład pod ręką?

+0

Cóż, twoja odpowiedź jest zdecydowanie lepsza niż moja =) +1 – justin

+1

Zawsze jest właściwe zwracanie bloku opartego na stosie i zawsze jest błąd zwracania bloku opartego na stosie? Podejrzewam, że jeden z nich powinien powiedzieć "kupa". –

+0

W 'zawsze jest prawidłowe zwracanie bloku opartego na stosie (i zawsze błąd zwracania bloku opartego na stosie) ", czy nie powinien on być _static_ blokiem w pierwszym przypadku? –