1

我正在尝试为 iOS 6 实施应用内购买,并且我正在关注Ray Wenderlich iOS 6 应用内教程 作为我的参考点。我对 Ray 的代码所做的一项重大更改是,我有一个按钮(应用程序购买中只有 1 个),用户可以点击该按钮进行购买,而不是为其制作自定义表格视图。我似乎无法让它工作,我一直在

-[__NSMallocBlock__ allObjects]: unrecognized selector sent to instance 0x1d5846d0
2012-10-03 00:03:25.715 myapp[752:907] *** Terminating app due to uncaught exception   'NSInvalidArgumentException', reason: '-[__NSMallocBlock__ allObjects]: unrecognized selector sent to instance 0x1d5846d0'

我相信问题发生在内部

- (void)requestProductsWithCompletionHandler:(RequestProductsCompletionHandler)completionHandler 

ray 已经实现并且我已经实现了 2 个辅助类。我为这些更改的只是应用程序 ID 号。这些类称为 IAPHelper.h/m 和 RageIAPHelper.h/m

我已经发布了我认为发生错误的代码(时间不长)。如果有人可以帮助我,我已经为此工作了大约 4 个小时。先感谢您

内部购买视图控制器

 -(void) viewDidLoad
 {
 [super viewDidLoad];
 [self reload]; //Customized for my own needs compared to what Ray had
  }

-(void)reload {
_products = nil;
[[RageIAPHelper sharedInstance] requestProductsWithCompletionHandler:^(BOOL success, NSArray *products) {
    if (success) {
        _products = products;
        //[self.tableView reloadData];
    }
    //[self.refreshControl endRefreshing];
 }];
}

称为 IAPHelper.m 的内部辅助方法

- (void)requestProductsWithCompletionHandler:(RequestProductsCompletionHandler)completionHandler {

// 1
 _completionHandler = [completionHandler copy];
// 2
_productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:_productIdentifiers]; // This is where I think the crash is
_productsRequest.delegate = self;
[_productsRequest start];
}
4

2 回答 2

4

The problem arises if

-(void)requestProdcutsWithCompletionHandler:(RequestProductsCompletionHandler)
    completionHandler{
    ....
{

is called twice before the first call completes. The reason is because the SKProductsRequestDelegate is used and calls the following function when it receives a response:

-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:
    (SKProductsResponse *)response{
    ...
    _completionHandler(YES, availableProducts)
    _completionHandler = nil;
}

Notice how _completionHandler is set to nil after the response is processed. Well, when the next response comes through from the duplicate call to "requestProductsWithCompletionHandler", _completionHandler is nil and throws the exception. You can fix this two ways, either make sure you do not call that function multiple times (bad way) or put some logic to make sure the completionHandler is not in use:

-(void)requestProductsWithCompletionHandler:(RequestProductsCompletionHandler)completionHandler{

NSLog(@"Request Products");
if (!_completionHandler) {
    NSLog(@"CompletionHandler called, new one was made");
    _completionHandler = [completionHandler copy];

    NSMutableSet * productIdentifiers = [NSMutableSet setWithCapacity:_products.count];
        for (IAPProduct * product in _products.allValues) {
            product.availableForPurchase = NO;
            [productIdentifiers addObject:product.productIdentifier];
        }

    _productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];
    _productsRequest.delegate=self;
    [_productsRequest start];
}else{
    NSLog(@"Duplicate call!");
}
}

productRequest first checks to make sure _completionHanlder is nil, and if not then it ignores the request since the handler is in use.

于 2012-12-18T03:48:14.843 回答
1

我决定为未来的读者发布答案而不是评论。我也在关注Ray Wenderlich教程,但在实现中发现了一个问题,这导致了我的崩溃。

他声明了几个有问题的实例变量(至少对我而言):

_productIdentifiers并且_purchasedProductIdentifiers 因为在单例的初始化中,他为它们分配了一个将被释放的对象。也许这是因为我的项目没有使用 ARC,但我认为没有。无论如何,如果您从他的教程中更改此代码:

// Store product identifiers
  /*_productIdentifiers = productIdentifiers; ----> This is the original code */
    _productIdentifiers = [[NSSet alloc]initWithSet:productIdentifiers];  // Replace with this

// Check for previously purchased products
  /*_purchasedProductIdentifiers = [NSMutableSet set]; ----> This is the original code*/
    _purchasedProductsIdentifiers = [[NSMutableSet alloc]init]; //Replace with this

因为其他方法释放对象。或者您可以在声明中声明属性,然后使用对象的自动发布版本。

我希望这对某人有所帮助,我花了一段时间才弄清楚(可能是因为我只是按照教程进行操作)。

于 2014-01-24T14:35:52.473 回答