You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

StoreKit二次请求并购买产品后崩溃问题求助

内购流程重复触发EXC_BAD_ACCESS崩溃的排查与修复

老哥,你遇到的这个EXC_BAD_ACCESS崩溃,一看就是典型的内存生命周期管理问题——毕竟是在两次进入内购页面、重复请求产品后触发购买才出现的,首次购买正常也印证了这一点。结合你的复现路径,我整理了几个高概率的排查方向和修复方案:

1. 管好SKProductsRequest的生命周期,避免野指针

每次打开页面都请求SKProducts,如果没正确处理请求的取消和代理引用,很容易导致已释放的对象被访问:

  • 在页面即将消失时,主动取消请求并置空:
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        productsRequest?.cancel()
        productsRequest = nil
    }
    
  • 声明请求对象时一定要用弱引用,避免循环引用让页面实例无法被释放:
    class PurchaseVC: UIViewController, SKProductsRequestDelegate {
        private weak var productsRequest: SKProductsRequest?
        // ... 其他代码
    }
    

2. 确保SKPaymentTransactionObserver注册/移除配对

如果你的页面作为交易观察者,退出时没移除,再次进入后重复注册,会导致内存混乱:

  • 在页面初始化时注册,销毁时移除:
    override func viewDidLoad() {
        super.viewDidLoad()
        SKPaymentQueue.default().add(self)
    }
    
    deinit {
        SKPaymentQueue.default().remove(self)
    }
    
  • 要是你用的是全局单例观察者,就不用每次进入页面都注册,但页面级的观察者必须严格配对操作。

3. 检查价格标签的UI更新逻辑

你提到要在标签上显示产品价格,多次进入页面后如果标签的引用已经失效,也会触发崩溃:

  • 所有UI更新必须在主线程执行,同时用[weak self]避免持有已释放的页面:
    func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
        DispatchQueue.main.async { [weak self] in
            guard let self = self else { return }
            // 更新价格标签
            if let product = response.products.first {
                self.priceLabel.text = product.localizedPrice
            }
        }
    }
    

4. 确保SKProduct实例有效

虽然你确认payment不为空,但如果创建payment用的SKProduct已经被释放,照样会出问题:

  • 把获取到的产品列表存在页面的强引用变量里,直到购买流程完成:
    class PurchaseVC: UIViewController {
        private var availableProducts: [SKProduct] = []
        // ...
        func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
            availableProducts = response.products
            // 更新UI展示价格
        }
        // 购买时用存储的product创建payment
        func triggerPurchase(at index: Int) {
            guard index < availableProducts.count else { return }
            let product = availableProducts[index]
            let payment = SKPayment(product: product)
            SKPaymentQueue.default().add(payment)
        }
    }
    

5. 用Zombie Objects精准定位问题

如果上面的方案都没解决,直接开Xcode的Zombie检测,一抓一个准:

  • 打开Product > Scheme > Edit Scheme
  • 切换到Run > Diagnostics标签
  • 勾选Zombie Objects
  • 重新运行复现崩溃,Xcode会明确告诉你是哪个对象被释放后又被访问了。

内容的提问来源于stack exchange,提问作者Alvaro Garcia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 07:03:49