在App Purchase Crashes on [[SKPaymentQueue defaultQueue] addPayment:payment] [英] In App Purchase Crashes on [[SKPaymentQueue defaultQueue] addPayment:payment]

查看:823
本文介绍了在App Purchase Crashes on [[SKPaymentQueue defaultQueue] addPayment:payment]的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的应用内购买项目。我提出一个带有购买UIButton的ModalView。您单击按钮,In App Purchase将通过该过程。

My In-App-Purchases work. I present a ModalView with a "Buy" UIButton. You click the button and the In App Purchase goes through the process. You can even do it several times in a row.

如果您打开模态视图,然后关闭模态视图(使用UITabBarButtonItem),然后重新打开模式查看并点击购买按钮。应用崩溃了,我得到一个NSZombie读取

The problem occurs if you open the Modal View, then close the Modal View (using a UITabBarButtonItem), then reopen the Modal View and tap the "Buy" button. The app crashes and I get an NSZombie that reads


* - [InAppPurchaseManager respondingToSelector:]:
deallocated instance 0x1c7ad0

* -[InAppPurchaseManager respondsToSelector:]: message sent to deallocated instance 0x1c7ad0

NSZombie指向.m文件中的第160行。我已标记了评论。

The NSZombie points to line 160 in the .m file. I have marked it with comments.

我从此页面获得了原始代码: http://troybrant.net/blog/2010/01/in-app-purchases-a-full-walkthrough/

I got the original code from this page: http://troybrant.net/blog/2010/01/in-app-purchases-a-full-walkthrough/

我一直在努力工作很多天,任何帮助将是真棒。

I have been struggling with this for many days now... any help would be awesome.

这里是.h



Here is the .h

//
//  InAppPurchaseManager.h
//  Copyright 2010 __MyCompanyName__. All rights reserved.


#import <UIKit/UIKit.h>
#import <StoreKit/StoreKit.h>

#define kInAppPurchaseManagerProductsFetchedNotification @"kInAppPurchaseManagerProductsFetchedNotification"
#define kInAppPurchaseManagerTransactionFailedNotification @"kInAppPurchaseManagerTransactionFailedNotification"
#define kInAppPurchaseManagerTransactionSucceededNotification @"kInAppPurchaseManagerTransactionSucceededNotification"

#define kInAppPurchaseCreditProductId @"com.myname.app.iap"

@interface InAppPurchaseManager : UIViewController <SKProductsRequestDelegate, SKPaymentTransactionObserver>
{
    SKProduct *productID;
    SKProductsRequest *productsRequest;

 IBOutlet UIBarButtonItem *closeButton;
 IBOutlet UIButton *buyButton;
 IBOutlet UILabel *testLabel;

}

@property (retain, nonatomic) SKProduct *productID;
@property (retain, nonatomic) SKProductsRequest *productsRequest;

@property (retain, nonatomic) IBOutlet UIBarButtonItem *closeButton;
@property (retain, nonatomic) IBOutlet UIButton *buyButton;
@property (retain, nonatomic) IBOutlet UILabel *testLabel;


// public methods
-(void)loadStore;
-(BOOL)canMakePurchases;
-(void)purchaseCredit;

-(void)requestInAppPurchaseData;
-(void)buyButtonAction:(id)sender;
-(void)closeButtonAction:(id)sender;
-(void)updateButtonStatus:(NSString *)status;

@end

这里是.m

// InAppPurchaseManager.m

#import "InAppPurchaseManager.h"

@implementation InAppPurchaseManager

@synthesize productID;
@synthesize productsRequest;

@synthesize closeButton;
@synthesize buyButton;
@synthesize testLabel;


- (void)dealloc {

 [productID release];
 //[productsRequest release];

 [closeButton release];
 [buyButton release];
 [testLabel release];

    [super dealloc];
}


- (void)viewDidLoad {
    [super viewDidLoad];

 [closeButton release];
 closeButton = [[UIBarButtonItem alloc] initWithTitle:@"Close" style:UIBarButtonItemStyleBordered target:self action:@selector(closeButtonAction:)];
 self.navigationItem.leftBarButtonItem = closeButton;

 [self loadStore];

 self.navigationItem.title = @"Credits";


}

-(void)closeButtonAction:(id)sender { 
 [self dismissModalViewControllerAnimated:YES];
}


-(void)buyButtonAction:(id)sender {

 if([self canMakePurchases]) {
  [self updateButtonStatus:@"OFF"];

  [self performSelectorOnMainThread:@selector(requestInAppPurchaseData) withObject:nil waitUntilDone:NO];

 } else {
  UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:nil message:[NSString stringWithString:@"Your account settings do not allow for In App Purchases."] delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
  [alertView show];
  [alertView release];  
 }

}


-(void)updateButtonStatus:(NSString *)status {

 if ([status isEqual:@"OFF"]) {
  closeButton.enabled = NO;
  buyButton.enabled = NO;
  buyButton.titleLabel.textColor = [UIColor grayColor];
 } else {
  closeButton.enabled = YES;
  buyButton.enabled = YES;
  buyButton.titleLabel.textColor = [UIColor blueColor];
 }

}

#pragma mark -
#pragma mark SKProductsRequestDelegate methods


//
// call this method once on startup
//
- (void)loadStore
{

    // restarts any purchases if they were interrupted last time the app was open
    [[SKPaymentQueue defaultQueue] addTransactionObserver:self];

}


- (void)requestInAppPurchaseData
{
 NSSet *productIdentifiers = [NSSet setWithObject:kInAppPurchaseCreditProductId];

    productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];
    productsRequest.delegate = self;
    [productsRequest start];

    // we will release the request object in the delegate callback
}



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

    NSArray *products = response.products;


    productID = [products count] == 1 ? [[products objectAtIndex:0] retain] : nil;
    if (productID)
    {
  /*
   NSLog(@"Product title: %@" , productID.localizedTitle);
   NSLog(@"Product description: %@" , productID.localizedDescription);
   NSLog(@"Product price: %@" , productID.price);
   NSLog(@"Product id: %@" , productID.productIdentifier);
   */

  NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
  NSString *currentCredits = ([standardUserDefaults objectForKey:@"currentCredits"]) ? [standardUserDefaults objectForKey:@"currentCredits"] : @"0";

  testLabel.text = [NSString stringWithFormat:@"%@", currentCredits];
    }

    for (NSString *invalidProductId in response.invalidProductIdentifiers)
    {
        //NSLog(@"Invalid product id: %@" , invalidProductId);
  testLabel.text = @"Try Again Later.";
    }

    // finally release the reqest we alloc/init’ed in requestProUpgradeProductData
    [productsRequest release];

    [[NSNotificationCenter defaultCenter] postNotificationName:kInAppPurchaseManagerProductsFetchedNotification object:self userInfo:nil];

 [self performSelectorOnMainThread:@selector(purchaseCredit) withObject:nil waitUntilDone:NO];
}


//
// call this before making a purchase
//
- (BOOL)canMakePurchases
{
    return [SKPaymentQueue canMakePayments];
}

//
// kick off the upgrade transaction
//
- (void)purchaseCredit
{

    SKPayment *payment = [SKPayment paymentWithProductIdentifier:kInAppPurchaseCreditProductId];

 // *********************************************************************************************************
 [[SKPaymentQueue defaultQueue] addPayment:payment]; // <--- This is where the NSZombie Appears *************
 // *********************************************************************************************************

}

#pragma -
#pragma Purchase helpers

//
// saves a record of the transaction by storing the receipt to disk
//
- (void)recordTransaction:(SKPaymentTransaction *)transaction
{
 if ([transaction.payment.productIdentifier isEqualToString:kInAppPurchaseCreditProductId])
    {
        // save the transaction receipt to disk
        [[NSUserDefaults standardUserDefaults] setValue:transaction.transactionReceipt forKey:@"InAppPurchaseTransactionReceipt" ];
        [[NSUserDefaults standardUserDefaults] synchronize];
    }

}

//
// enable pro features
//
- (void)provideContent:(NSString *)productId
{
 if ([productId isEqualToString:kInAppPurchaseCreditProductId])
    {        
  // Increment currentCredits
  NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
  NSString *currentCredits = [standardUserDefaults objectForKey:@"currentCredits"];
  int newCreditCount = [currentCredits intValue] + 1;
  [standardUserDefaults setObject:[NSString stringWithFormat:@"%d", newCreditCount] forKey:@"currentCredits"];

  testLabel.text = [NSString stringWithFormat:@"%d", newCreditCount];

    }

}

//
// removes the transaction from the queue and posts a notification with the transaction result
//
- (void)finishTransaction:(SKPaymentTransaction *)transaction wasSuccessful:(BOOL)wasSuccessful
{

    // remove the transaction from the payment queue.
    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];

    NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:transaction, @"transaction" , nil];
    if (wasSuccessful)
    {
        // send out a notification that we’ve finished the transaction
        [[NSNotificationCenter defaultCenter] postNotificationName:kInAppPurchaseManagerTransactionSucceededNotification object:self userInfo:userInfo];
    }
    else
    {
        // send out a notification for the failed transaction
        [[NSNotificationCenter defaultCenter] postNotificationName:kInAppPurchaseManagerTransactionFailedNotification object:self userInfo:userInfo];
    }


 [self updateButtonStatus:@"ON"];

}

//
// called when the transaction was successful
//
- (void)completeTransaction:(SKPaymentTransaction *)transaction
{

 [self updateButtonStatus:@"OFF"];

 [self recordTransaction:transaction];
    [self provideContent:transaction.payment.productIdentifier];
 [self finishTransaction:transaction wasSuccessful:YES];

}

//
// called when a transaction has been restored and and successfully completed
//
- (void)restoreTransaction:(SKPaymentTransaction *)transaction
{
    [self recordTransaction:transaction.originalTransaction];
    [self provideContent:transaction.originalTransaction.payment.productIdentifier];
    [self finishTransaction:transaction wasSuccessful:YES];
}

//
// called when a transaction has failed
//
- (void)failedTransaction:(SKPaymentTransaction *)transaction
{

    if (transaction.error.code != SKErrorPaymentCancelled)
    {
   // error!
        [self finishTransaction:transaction wasSuccessful:NO];
    }
    else
    {
   // this is fine, the user just cancelled, so don’t notify
        [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
    }

 [self updateButtonStatus:@"ON"];

}

#pragma mark -
#pragma mark SKPaymentTransactionObserver methods

//
// called when the transaction status is updated
//
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{

    for (SKPaymentTransaction *transaction in transactions)
    {
        switch (transaction.transactionState)
        {
            case SKPaymentTransactionStatePurchased:
                [self completeTransaction:transaction];
                break;
            case SKPaymentTransactionStateFailed:
                [self failedTransaction:transaction];
                break;
            case SKPaymentTransactionStateRestored:
                [self restoreTransaction:transaction];
                break;
            default:
                break;
        }
    }
}


@end


推荐答案

错误消息表示正在向 InAppPurchaseManager 的释放实例发送消息,类。它在你打开视图(创建一个实例),关闭视图(释放一个实例),然后再次打开视图(创建第二个实例)之后发生。并且问题发生在 addPayment:调用中。这表示框架仍然有一个句柄在你的老的,释放的实例,并试图发送一个消息。

The error message indicates a message is being sent to a deallocated instance of InAppPurchaseManager, which is your class. And it's happening after you open the view (creating an instance), close the view (releasing an instance), then opening the view again (creating a second instance). And the problem is happening within the addPayment: call. This indicates that the framework still has a handle on your old, released instance, and is trying to send it a message.

你给框架一个句柄你的对象loadStore,当你调用

You give the framework a handle to your object in loadStore, when you call

[[SKPaymentQueue defaultQueue] addTransactionObserver:self];

我看不到您移除的地方 self 作为观察者。发送通知的对象通常不会保留其观察者,因为这样做会创建保留周期和/或内存泄漏。

I don't see anywhere where you remove self as an observer. Objects that send out notifications usually do not retain their observers, since doing so can create a retain cycle and/or a memory leak.

在您的 dealloc 代码,你需要清理并调用 removeTransactionObserver:。这应该可以解决你的问题。

In your dealloc code you need to cleanup and call removeTransactionObserver:. That should solve your problem.

这篇关于在App Purchase Crashes on [[SKPaymentQueue defaultQueue] addPayment:payment]的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆