Work with my applications. I am introducing ModalView using the "Buy" UIButton. You press the button and the “In App Purchase” application goes through the process. You can do this several times in a row.
The problem arises if you open Modal View, then close Modal View (using UITabBarButtonItem), then open Modal View again and click the Buy button. The application crashes and I get an NSZombie that reads
*** - [InAppPurchaseManager responds with SoSelector:]: message sent to deallocated instance 0x1c7ad0
NSZombie points to line 160 in the .m file. I noted with his comments.
I got the source 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 ... any help would be awesome.
Here is the .h
// // InAppPurchaseManager.h // Copyright 2010 __MyCompanyName__. All rights reserved. #import <UIKit/UIKit.h>
Here ..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
iphone crash nszombie in-app-purchase
Chris Nov 11 '10 at 2:30 2010-11-11 02:30
source share