title: In App Purchase Crashes on [[SKPaymentQueue defaultQueue] addPayment:payment]
body: <p>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.</p>
  
  <p>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 </p>
  
  <blockquote>
    <p>*** -[InAppPurchaseManager respondsToSelector:]: message sent to
    deallocated instance 0x1c7ad0</p>
  </blockquote>
  
  <p>The NSZombie points to line 160 in the .m file. I have marked it with comments.</p>
  
  <p>I got the original code from this page: <a href="http://troybrant.net/blog/2010/01/in-app-purchases-a-full-walkthrough/" rel="noreferrer">http://troybrant.net/blog/2010/01/in-app-purchases-a-full-walkthrough/</a></p>
  
  <p>I have been struggling with this for many days now... any help would be awesome.</p>
  
  <p>Here is the .h</p>
  
  <pre><code>//
  //  InAppPurchaseManager.h
  //  Copyright 2010 __MyCompanyName__. All rights reserved.
  
  
  #import &lt;UIKit/UIKit.h&gt;
  #import &lt;StoreKit/StoreKit.h&gt;
  
  #define kInAppPurchaseManagerProductsFetchedNotification @"kInAppPurchaseManagerProductsFetchedNotification"
  #define kInAppPurchaseManagerTransactionFailedNotification @"kInAppPurchaseManagerTransactionFailedNotification"
  #define kInAppPurchaseManagerTransactionSucceededNotification @"kInAppPurchaseManagerTransactionSucceededNotification"
  
  #define kInAppPurchaseCreditProductId @"com.myname.app.iap"
  
  @interface InAppPurchaseManager : UIViewController &lt;SKProductsRequestDelegate, SKPaymentTransactionObserver&gt;
  {
      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
  </code></pre>
  
  <p>Here is the .m</p>
  
  <pre><code>// 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]; // &lt;--- 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
  </code></pre>