2014-08-21 15 views
6

Ich arbeite an einigen IAPs using this tutorial.Completion Handler verursacht EXC_BAD_ACCESS, wenn dieselbe Methode zweimal aufgerufen wird

Zuerst hole ich die Produkte mit diesem:

-(void)fetchAvailableProductsFirstLoad:(BOOL)firstTimeLoading { 
    [[IAPHelper sharedInstance] requestProductsWithCompletionHandler:^(BOOL success, NSArray *products) { ... 

Der Helfer läuft der folgende:

- (void)requestProductsWithCompletionHandler:(RequestProductsCompletionHandler)completionHandler { 

    @synchronized(self) { 
     // 1 
     _completionHandler = [completionHandler copy]; 

     // 2 
     _productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:_productIdentifiers]; 
     _productsRequest.delegate = self; 
     [_productsRequest start]; 
    } 
} 

Wenn die Produkte zurückgegeben werden oder nicht die folgenden genannt:

#pragma mark - SKProductsRequestDelegate 

- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response { 

    NSLog(@"Loaded list of products..."); 
    _productsRequest = nil; 

    NSArray * skProducts = response.products; 
    for (SKProduct * skProduct in skProducts) { 
     NSLog(@"Found product: %@ %@ %0.2f", 
       skProduct.productIdentifier, 
       skProduct.localizedTitle, 
       skProduct.price.floatValue); 
    } 

    _completionHandler(YES, skProducts); 
    _completionHandler = nil; 

} 

- (void)request:(SKRequest *)request didFailWithError:(NSError *)error { 

    NSLog(@"Failed to load list of products."); 
    NSLog(@"Error: %@",error); 
    _productsRequest = nil; 

    _completionHandler(NO, nil); 
    _completionHandler = nil; 

} 

Ausgabe
Das Problem, das wir haben, ist, wenn der Benutzer einen Abruf oder Produkte zweimal startet. Zum Beispiel werden die Fetch-Produkte auf der ViewDidLoad aufgerufen, aber wenn der Benutzer eine schlechte/langsame Verbindung hat und weg navigiert und dann zurück zum Controller. Der erste Abruf wird nicht abgebrochen, daher werden zwei ausgeführt.

Ich glaube, das Problem ist, wenn die zweite zurückgegeben wird und der Zeiger geändert hat/nicht vorhanden/beschädigt ist.

Der EXC_BAD_ACCESS Code 2 Fehler tritt auf der entsprechenden Zeile:

_completionHandler(YES, skProducts); 

ODER

_completionHandler(NO, nil); 

Antwort

14

Du hast Recht. Sie ist nicht vorhanden, wenn die zweite Antwort zurückgegeben wird, da sie nach der Verarbeitung der ersten Antwort ungültig ist: completionHandler = nil.

In einer solchen Situation, finde ich es am sichersten, immer zu prüfen, ob der Block es vor dem Aufruf existiert:

if (_completionHandler) { 
    _completionHandler(YES, skProducts); 
    _completionHandler = nil; 
} 

(und das gleiche in -request:didFailWithError:). In der aktuellen Implementierung würde der Aufruf [[IAPHelper sharedInstance] requestProductsWithCompletionHandler:nil] ohne diese Prüfung den gleichen Absturz verursachen (probier es aus!).

Zusätzlich zu diesen Sicherheitsprüfungen ist es am besten, Ihre erste Anfrage gegebenenfalls zu stornieren, z. B. wenn der Benutzer navigiert und die Antwort trotzdem nicht angezeigt wird. Auch in -requestProductsWithCompletionHandler:, entweder eine vorhandene _productsRequest vor der Erstellung der neuen oder die Überprüfung auf eine vorhandene _productsRequest zu entscheiden, ob eine neue zu erstellen oder zu löschen, wäre eine weitere nützliche Sicherheitsschicht.

+1

Danke, ich habe Teile von beiden Antworten verwendet, aber das war am nächsten. Ich fügte auch eine zusätzliche Methode hinzu '- (void) cancelProductRequest { [_productsRequest cancel]; _productsRequest = Null; } ', die die aktuelle Anfrage abbricht, wenn sie von diesem Controller weg navigieren, um dort zu speichern, wo immer mehrere Anfragen sind. – StuartM

+0

Great @StuartM, definitiv am besten, um die Anfrage abzubrechen, wenn Sie es nicht brauchen. Froh, dass ich helfen konnte! – stefandouganhyde