ios – 在应用程序购买时更改场景时崩溃的应用程序
我已经在我的商店场景中为我的游戏实施了应用程序购买,并且在从商店场景转换到另一个场景时遇到了问题似乎让游戏崩溃并且给了我这个错误
Thread 1: EXC_BAD_ACCESS (code=1,address=0x840f8010) 或者它给了我其他错误的多个版本,例如: Thread 1: EXC_BAD_ACCESS (code=1,address=0x3f8) 它有时会给我一个错误,改变子类中的行,就像这样 当我在子类中注释掉搜索应用购买信息或将手机置于飞行模式时,它工作正常,问题就消失了. 我有一个SKnode的子类,它获取有关可购买产品的信息,然后通过使用SKLabels和sprite节点显示它,以便在商店中显示购买的图片,如下所示: class InAppPurchaseItems: SKNode,SKProductsRequestDelegate { var shopItemNode = SKSpriteNode() var itemPriceBackground = SKSpriteNode() var shopItemLabel = SKLabelNode() var shopItemTitleLabel = SKLabelNode() var pressableNode = SKSpriteNode() var itemPriceLabel = SKLabelNode() var title: String = "" var information: String = "" var image: String = "" var price:String = "X" func createAppPurchaseItem(ID: String,purchaseImage: String,purchaseTitle:String) { title = purchaseTitle image = purchaseImage createTheNode() //let product = SKProduct() let productID: NSSet = NSSet(objects: ID) //"RedShield.Astrum.Purchase","DoubleCoin.Astrum.Purchase") let request: SKProductsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>) request.delegate = self as? SKProductsRequestDelegate request.start() } public func productsRequest(_ request: SKProductsRequest,didReceive response: SKProductsResponse) { print("product request...") let myProduct = response.products for product in myProduct { print("product added") if product.productIdentifier == "RedShield.Astrum.Purchase" { price = priceStringForProduct(item: product)! information = product.localizedDescription } else if product.productIdentifier == "DoubleCoin.Astrum.Purchase" { price = priceStringForProduct(item: product)! information = product.localizedDescription } else if product.productIdentifier == "1500Stars.Astrum.Purchase" { price = priceStringForProduct(item: product)! shopItemNode.texture = SKTexture(imageNamed: "BuyStarsTeirOne") } else if product.productIdentifier == "7500Stars.Astrum.Purchase" { price = priceStringForProduct(item: product)! shopItemNode.texture = SKTexture(imageNamed: "BuyStarsTeirTwo") } else if product.productIdentifier == "14000Stars.Astrum.Purchase" { price = priceStringForProduct(item: product)! shopItemNode.texture = SKTexture(imageNamed: "BuyStarsTeirThree") } else if product.productIdentifier == "28000Stars.Astrum.Purchase" { price = priceStringForProduct(item: product)! shopItemNode.texture = SKTexture(imageNamed: "BuyStarsTeirFour") } else if product.productIdentifier == "65000Stars.Astrum.Purchase" { price = priceStringForProduct(item: product)! shopItemNode.texture = SKTexture(imageNamed: "BuyStarsTeirFive") } else if product.productIdentifier == "128000Stars.Astrum.Purchase" { price = priceStringForProduct(item: product)! shopItemNode.texture = SKTexture(imageNamed: "BuyStarsTeirSix") } createShopLabels() } } func priceStringForProduct(item: SKProduct) -> String? { let price = item.price if price == 0 { return "GET" //or whatever you like } else { let numberFormatter = NumberFormatter() let locale = item.priceLocale numberFormatter.numberStyle = .currency numberFormatter.locale = locale return numberFormatter.string(from: price) } } func createTheNode() { let tex:SKTexture = SKTexture(imageNamed: image) shopItemNode = SKSpriteNode(texture: tex,color: SKColor.black,size: CGSize(width: 85,height: 85)) //frame.maxX / 20,height: frame.maxY / 20)) shopItemNode.zPosition = -10 shopItemNode.position = CGPoint(x: 0,y: 35) self.addChild(shopItemNode) self.name = "ShopItem" self.zPosition = -11 shopItemTitleLabel = SKLabelNode(fontNamed: "Avenir-Black") shopItemTitleLabel.fontColor = UIColor.black; shopItemTitleLabel.fontSize = 15 //self.frame.maxY/30 shopItemTitleLabel.position = CGPoint (x: 0,y: -30) shopItemTitleLabel.text = "(title)" shopItemTitleLabel.zPosition = -9 self.addChild(shopItemTitleLabel) itemPriceBackground = SKSpriteNode(texture: SKTexture(imageNamed: "PriceShopBackground"),color: .clear,size: CGSize(width: 80,height: 30)) //SKSpriteNode(color: SKColor.black,size: CGSize(width: 65,height: 20)) //itemPriceBackground.alpha = 0.4 itemPriceBackground.zPosition = -10 itemPriceBackground.position = CGPoint(x: 0,y: -54) addChild(itemPriceBackground) pressableNode = SKSpriteNode(texture: nil,size: CGSize(width: 100,height: 140)) pressableNode.zPosition = -7 pressableNode.position = CGPoint(x: 0,y: 0) shopItemSprites.append(pressableNode) addChild(pressableNode) } func createShopLabels() { shopItemLabel = SKLabelNode(fontNamed: "Avenir-Black") shopItemLabel.fontColor = UIColor.white; shopItemLabel.fontSize = 15 //self.frame.maxY/30 shopItemLabel.position = CGPoint (x: 0,y: -60) shopItemLabel.text = "(price)" shopItemLabel.zPosition = -9 addChild(shopItemLabel) } } 然后使用以下代码在商店场景中显示它们: let ShopItem = InAppPurchaseItems() ShopItem.createAppPurchaseItem(ID: "DoubleCoin.Astrum.Purchase",purchaseImage: "2StarCoin",purchaseTitle: "+2 In Game Pickups") ShopItem.position = CGPoint(x: self.frame.midX / 1.6,y: self.frame.midY * 0.8) ShopItem.zPosition = 100 ShopItem.name = "Shp0" moveableArea.addChild(ShopItem) 商店的主类 商店主类还有应用程序购买代码,用于购买产品,也能够像子类中一样搜索产品信息,如下所示 class ShopItemMenu: SKScene,SKProductsRequestDelegate,SKPaymentTransactionObserver { //Purchase Variables var listOfProducts = [SKProduct]() var p = SKProduct() override func didMoveToView(to view: SKView) { let ShopItem = InAppPurchaseItems() ShopItem.createAppPurchaseItem(ID: "DoubleCoin.Astrum.Purchase",y: self.frame.midY * 0.8) ShopItem.zPosition = 100 ShopItem.name = "Shp0" moveableArea.addChild(ShopItem) } //This function allows for a product to be bought buy the user and starts the proccess for purchasing func appPurchaseBuying(appPurchaseID:String) { for product in listOfProducts { let prodID = product.productIdentifier if(prodID == appPurchaseID) { p = product buyProduct() } } } //This Function restores all previously purchased Items (use this for the restor button. func restorePurchasesOfItems() { SKPaymentQueue.default().add(self) SKPaymentQueue.default().restoreCompletedTransactions() } //This function checks if they can make payments and then loads the product ids from a harcoded set. (use this to start when the scene starts) func checkCanMakePayment() { if (SKPaymentQueue.canMakePayments()) { print("can make payments...") let productID: NSSet = NSSet(objects: "RedShield.Astrum.Purchase","DoubleCoin.Astrum.Purchase") let request: SKProductsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>) request.delegate = self request.start() } else { let alert = UIAlertController(title: "In-App Purchases Not Enabled",message: "Please enable In App Purchases in Settings",preferredStyle: UIAlertControllerStyle.alert) alert.addAction(UIAlertAction(title: "Settings",style: UIAlertActionStyle.default,handler: { alertAction in alert.dismiss(animated: true,completion: nil) let url: NSURL? = NSURL(string: UIApplicationOpenSettingsURLString) if url != nil { UIApplication.shared.openURL(url! as URL) } })) alert.addAction(UIAlertAction(title: "Ok",completion: nil) })) if let vc = self.scene?.view?.window?.rootViewController { vc.present(alert,animated: true,completion: nil) } } } //This allows the user to buy the product with a product idetifier given by the variable "p" func buyProduct() { print("buying " + p.productIdentifier) let pay = SKPayment(product: p) SKPaymentQueue.default().add(self) SKPaymentQueue.default().add(pay as SKPayment) } //This Function gets all the avaliable products from apple and puts them into the product Array called listOfProducts public func productsRequest(_ request: SKProductsRequest,didReceive response: SKProductsResponse) { /* print("product request...") let myProduct = response.products for product in myProduct { print("product added") if product.productIdentifier == "RedShield.Astrum.Purchase" { shieldPurchasePrice = priceStringForProduct(item: product)! } else if product.productIdentifier == "DoubleCoin.Astrum.Purchase" { DoubleCoinPurchasePrice = priceStringForProduct(item: product)! } /*print(product.productIdentifier) print(product.localizedTitle) print(product.localizedDescription) print(product.price) */ listOfProducts.append(product) }*/ } func priceStringForProduct(item: SKProduct) -> String? { let price = item.price if price == 0 { return "GET" //or whatever you like } else { let numberFormatter = NumberFormatter() let locale = item.priceLocale numberFormatter.numberStyle = .currency numberFormatter.locale = locale return numberFormatter.string(from: price) } } //This Function restores all the already purchased products so that things can be restored such as shield public func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) { print("restoring all...") for transaction in queue.transactions { let t: SKPaymentTransaction = transaction let prodID = t.payment.productIdentifier as String switch prodID { case "RedShield.Astrum.Purchase": isRedShieldPurchaSEOn = true let defaults = UserDefaults.standard defaults.set(isRedShieldPurchaSEOn,forKey: "shieldPurchase") print("finished restoring this purchase") case "DoubleCoin.Astrum.Purchase": isCoinPurchaSEOn = true let defaults = UserDefaults.standard defaults.set(isCoinPurchaSEOn,forKey: "doubleCoinPurchase") print("finished restoring this purchase") default: print("IAP not found") } } alert(title: "Restored",msg: "Purchases were restored") } //This Function is run when the user makes a purchase and checks the state of the purchase to make sure it works public func paymentQueue(_ queue: SKPaymentQueue,updatedTransactions transactions: [SKPaymentTransaction]) { print("adding payment...") for transaction: AnyObject in transactions { let trans = transaction as! SKPaymentTransaction print(trans.error) switch trans.transactionState { case .purchased: print("buying ok,Unlocking purchase...") print(p.productIdentifier) let prodID = p.productIdentifier switch prodID { case "RedShield.Astrum.Purchase": isRedShieldPurchaSEOn = true let defaults = UserDefaults.standard defaults.set(isRedShieldPurchaSEOn,forKey: "shieldPurchase") print("unlocked Purchase") case "DoubleCoin.Astrum.Purchase": isCoinPurchaSEOn = true let defaults = UserDefaults.standard defaults.set(isCoinPurchaSEOn,forKey: "doubleCoinPurchase") print("unlocked Purchase") case "SOME IN APP PURCHASE ID HERE": print("unlocked Purchase") default: print("IAP Not found") } queue.finishTransaction(trans) case .failed: print("error with payment...") queue.finishTransaction(trans) default: print("Default") } } } 我是以正确的方式做这件事还是有更好的方法来做到这一点,我如何解决我遇到的崩溃问题? 编辑 编辑2 编辑3 编辑4 import Foundation import SpriteKit import StoreKit class PurchaseService { static let session = PurchaseService() var products = [SKProduct]() var p = SKProduct() //This function allows for a product to be bought buy the user and starts the proccess for purchasing func appPurchaseBuying(appPurchaseID:String) { for product in products { let prodID = product.productIdentifier if(prodID == appPurchaseID) { p = product buyProduct() } } } //This Function restores all previously purchased Items (use this for the restor button. func restorePurchasesOfItems() { //SKPaymentQueue.default().add(self) SKPaymentQueue.default().restoreCompletedTransactions() } //This function checks if they can make payments and then loads the product ids from a harcoded set. (use this to start when the scene starts) func checkCanMakePayment() { if (SKPaymentQueue.canMakePayments()) { print("can make payments...") let productID: NSSet = NSSet(objects: "RedShield.Astrum.Purchase","DoubleCoin.Astrum.Purchase") let request: SKProductsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>) //request.delegate = self request.start() } else { let alert = UIAlertController(title: "In-App Purchases Not Enabled",completion: nil) })) } } //This allows the user to buy the product with a product idetifier given by the variable "p" func buyProduct() { print("buying " + p.productIdentifier) let pay = SKPayment(product: p) //SKPaymentQueue.default().add(self) SKPaymentQueue.default().add(pay as SKPayment) } //This Function gets all the avaliable products from apple and puts them into the product Array called listOfProducts public func productsRequest(_ request: SKProductsRequest,didReceive response: SKProductsResponse) { print("product request...") let myProduct = response.products for product in myProduct { print("product added") products.append(product) } } func priceStringForProduct(item: SKProduct) -> String? { let price = item.price if price == 0 { return "GET" //or whatever you like } else { let numberFormatter = NumberFormatter() let locale = item.priceLocale numberFormatter.numberStyle = .currency numberFormatter.locale = locale return numberFormatter.string(from: price) } } //This Function restores all the already purchased products so that things can be restored such as shield public func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) { print("restoring all...") for transaction in queue.transactions { let t: SKPaymentTransaction = transaction let prodID = t.payment.productIdentifier as String switch prodID { case "RedShield.Astrum.Purchase": isRedShieldPurchaSEOn = true let defaults = UserDefaults.standard defaults.set(isRedShieldPurchaSEOn,forKey: "doubleCoinPurchase") print("finished restoring this purchase") default: print("IAP not found") } } //alert(title: "Restored",Unlocking purchase...") print(p.productIdentifier) let prodID = p.productIdentifier switch prodID { case "RedShield.Astrum.Purchase": isRedShieldPurchaSEOn = true let defaults = UserDefaults.standard defaults.set(isRedShieldPurchaSEOn,forKey: "shieldPurchase") print("unlocked Purchase") case "DoubleCoin.Astrum.Purchase": isCoinPurchaSEOn = true let defaults = UserDefaults.standard defaults.set(isCoinPurchaSEOn,forKey: "doubleCoinPurchase") print("unlocked Purchase") case "SOME IN APP PURCHASE ID HERE": print("unlocked Purchase") default: print("IAP Not found") } queue.finishTransaction(trans) case .failed: print("error with payment...") queue.finishTransaction(trans) default: print("Default") } } } } 解决方法
在游戏场景中拥有所有StoreKit代码会使您更难以找到您遇到的问题.我建议你创建一个新的swift文件,让我们用这样的静态实例称它为PurchaseService:
class PurchaseService { static let session = PurchaseService() var products = [SKProduct]() // code } 您可以在此处实施所有与采购相关的逻辑.我通常使用getPurchases函数从商店加载可用的购买并从AppDelegate.swift文件的应用程序函数调用它.这可以确保您的购买很早就加载,并在您需要的第一时间准备好(因为您创建了一个静态实例,您可以在需要通过PurchaseService.session进行购买时参考它…) 要获得价格,您可以使用迭代产品变量的函数并检查产品ID: func price(for productID:String)->Double{ if products.count>0 { for product in products { if product.productIdentifier == productID { return product.price.doubleValue } } } } 如果您遵守SKProductRequestDelegate协议,则无需有条件地将自己投射到该协议: // unnecessary: request.delegate = self as? SKProductsRequestDelegate request.delegate = self 想知道您是否公开了productRequest方法,因为在请求返回SKProductResponse对象时,self不再可用. 关于项目中的Objective-C代码:我看到你可能正在使用Firebase(来自我推断的控制台消息)并且它有一些客观的c部分. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |