完成处理程序在两次调用相同方法时导致EXC_BAD_ACCESS

Stu*_*rtM 6 exc-bad-access storekit in-app-purchase ios skproduct

我正在使用本教程研究一些IAP .

首先我用这个获取产品:

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

帮助程序运行以下内容:

- (void)requestProductsWithCompletionHandler:(RequestProductsCompletionHandler)completionHandler {

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

        // 2
        _productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:_productIdentifiers];
        _productsRequest.delegate = self;
        [_productsRequest start];
    }
}
Run Code Online (Sandbox Code Playgroud)

当产品退回或失败时,会调用以下内容:

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

}
Run Code Online (Sandbox Code Playgroud)

发出
当用户启动一个获取或产品的两倍,我们有问题的.例如,在viewDidLoad上调用fetch产品,但是如果用户具有错误/慢速连接并导航,然后返回控制器.初始提取未被取消,因此有两个正在运行.

我相信问题是当第二个返回并且指针已经改变/不存在/损坏时.

EXC_BAD_ACCESS代码2错误发生在相关行上:

_completionHandler(YES, skProducts);
Run Code Online (Sandbox Code Playgroud)

要么

_completionHandler(NO, nil);
Run Code Online (Sandbox Code Playgroud)

ste*_*yde 17

你是对的.返回第二个响应时它不存在,因为它在处理完第一个响应后被填满:completionHandler = nil.

在这种情况下,我觉得在调用之前总是检查块是否最安全:

if (_completionHandler) {
    _completionHandler(YES, skProducts);
    _completionHandler = nil;
}
Run Code Online (Sandbox Code Playgroud)

(和相同的-request:didFailWithError:).在您当前的实现中,调用[[IAPHelper sharedInstance] requestProductsWithCompletionHandler:nil]将导致相同的崩溃而不进行此检查(尝试它!).

除了这些安全检查之外,最好在适当时取消您的第一个请求,例如当用户导航时,无论如何都不会看到响应.此外,-requestProductsWithCompletionHandler:要么_productsRequest在创建新现有产品之前取消现有产品,要么检查现有产品_productsRequest是否要创建新产品,这将是另一个有用的安全层.