ver suscripcion restringidas las facturacion estan dentro datos compras cancelar bill apple app iphone crash in-app-purchase nszombie

iphone - suscripcion - las compras dentro de la app estan restringidas



En Bloqueos de compra de aplicaciones en (2)

El mensaje de error indica que se está enviando un mensaje a una instancia desasignada de InAppPurchaseManager , que es su clase. Y sucede después de que abre la vista (creando una instancia), cierra la vista (liberando una instancia) y luego vuelve a abrir la vista (creando una segunda instancia). Y el problema está sucediendo dentro de addPayment: call. Esto indica que el marco todavía tiene un control sobre su instancia anterior lanzada y está intentando enviarle un mensaje.

Usted le da un marco a la estructura de su objeto en loadStore, cuando llama

[[SKPaymentQueue defaultQueue] addTransactionObserver:self];

No veo ningún lugar donde te elimines como observador. Los objetos que envían notificaciones generalmente no retienen a sus observadores, ya que al hacerlo pueden crear un ciclo de retención y / o una pérdida de memoria.

En su código dealloc necesita limpiar y llamar a removeTransactionObserver: Eso debería resolver tu problema.

Mi trabajo en la aplicación funciona. Presento un ModalView con un UIButton "Comprar". Hace clic en el botón y la compra en la aplicación pasa por el proceso. Incluso puedes hacerlo varias veces seguidas.

El problema ocurre si abre la Vista modal, luego cierra la Vista modal (usando un UITabBarButtonItem), luego vuelve a abrir la Vista modal y toca el botón "Comprar". La aplicación falla y obtengo un NSZombie que lee

*** - [InAppPurchaseManager respondsToSelector:]: mensaje enviado a la instancia desasignada 0x1c7ad0

El NSZombie apunta a la línea 160 en el archivo .m. Lo he marcado con comentarios.

Obtuve el código original de esta página: http://troybrant.net/blog/2010/01/in-app-purchases-a-full-walkthrough/

He estado luchando con esto durante muchos días ... cualquier ayuda sería increíble.

Aquí está el .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

Aquí está el .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


Creo que los observadores añadidos usando addTransactionObserver son aparentemente referencias débiles, no fuertes, lo que explicaría esto. Hice una prueba simple:

// bad code below: // the reference is weak so the observer is immediately destroyed addTransactionObserver([[MyObserver alloc] init]); ... [[SKPaymentQueue defaultQueue] addPayment:payment]; // crash

Y obtuve el mismo bloqueo incluso sin llamar a removeTransactionObserver. La solución en mi caso era simplemente mantener una fuerte referencia al observador:

@property (strong) MyObserver* observer; .... self.observer = [[MyObserver alloc] init]; addTransactionObserver(observer);