6

このチュートリアルを使用して、いくつかの IAP に取り組んでいます。

まず、これで製品を取得します:

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

ヘルパーは以下を実行します。

- (void)requestProductsWithCompletionHandler:(RequestProductsCompletionHandler)completionHandler {

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

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

製品が返品または失敗した場合、以下が呼び出されます。

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

}

問題
問題は、ユーザーがフェッチまたは製品を 2 回開始した場合です。たとえば、フェッチ製品はviewDidLoadで呼び出されますが、ユーザーの接続が悪い/遅い場合は、コントローラーから離れてからコントローラーに戻ります。初期フェッチはキャンセルされないため、2 つ実行されています。

問題は、秒が返され、ポインターが変更された/存在しない/破損した場合だと思います。

関連する行で EXC_BAD_ACCESS コード 2 エラーが発生します。

_completionHandler(YES, skProducts);

また

_completionHandler(NO, nil);
4

1 に答える 1

17

あなたが正しい。最初の応答が処理された後に nil されるため、2 番目の応答が返されたときには存在しませんcompletionHandler = nil

この種の状況では、ブロックを呼び出す前にブロックが存在することを常に確認するのが最も安全だと思います。

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

(および で同じ-request:didFailWithError:)。あなたの現在の実装では、呼び出し[[IAPHelper sharedInstance] requestProductsWithCompletionHandler:nil]はこのチェックなしで同じクラッシュを引き起こします (試してみてください!)。

これらの安全性チェックに加えて、ユーザーが移動しても応答が表示されない場合など、必要に応じて最初の要求をキャンセルすることをお勧めします。また、 では、新しいものを作成する前に-requestProductsWithCompletionHandler:既存のものをキャンセルするか、新しいものを作成するかどうかを決定するために既存のものを確認することは、別の有用な安全層になります。_productsRequest_productsRequest

于 2014-08-21T13:21:41.753 に答える