Я работаю над некоторыми 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;
}
Проблема
У нас возникает проблема, когда пользователь дважды запускает выборку или продукты. Например, выборка продуктов вызывается в viewDidLoad, но если у пользователя плохое / медленное соединение, и он уходит, а затем возвращается к контроллеру. Первоначальная выборка не отменяется, поэтому выполняется две операции.
Я считаю, что проблема в том, что возвращается второй и указатель изменился / не существует / поврежден.
Ошибка кода 2 EXC_BAD_ACCESS возникает в соответствующей строке:
_completionHandler(YES, skProducts);
ИЛИ
_completionHandler(NO, nil);
1 ответ
Ты прав. Его не существует, когда возвращается второй ответ, потому что он обнуляется после обработки первого ответа: completionHandler = nil
.
В такой ситуации я считаю безопасным всегда проверять, существует ли блок перед его вызовом:
if (_completionHandler) {
_completionHandler(YES, skProducts);
_completionHandler = nil;
}
(и то же самое в -request:didFailWithError:
). В вашей текущей реализации вызов [[IAPHelper sharedInstance] requestProductsWithCompletionHandler:nil]
вызвал бы такой же сбой без этой проверки (попробуйте!).
Помимо этих проверок безопасности, было бы лучше отменить ваш первый запрос, когда это необходимо, например, когда пользователь перемещается и все равно не видит ответа. Кроме того, в -requestProductsWithCompletionHandler:
, либо отмена существующего _productsRequest
перед созданием нового, либо проверка существующего _productsRequest
, чтобы решить, создавать ли новый, будет еще одним полезным слоем безопасность.
Похожие вопросы
Новые вопросы
ios
iOS - мобильная операционная система, работающая на Apple iPhone, iPod touch и iPad. Используйте этот тег [ios] для вопросов, связанных с программированием на платформе iOS. Используйте связанные теги [target-c] и [swift] для проблем, характерных для этих языков программирования.
-(void)cancelProductRequest { [_productsRequest cancel]; _productsRequest = nil; }
, который отменяет текущий запрос, если они уходят от этого контроллера, чтобы сохранить несколько запросов.