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

如何在C++ std::map中正确存储Objective-C的SKProduct*?

问题分析与解答

你在使用std::map<std::string, SKProduct*>存储和检索SKProduct*时存在多处错误,包括逻辑错误、引用计数管理错误,这些问题直接导致了objc_retain_x0崩溃,以下是具体分析和修正方案:


存在的核心错误

1. 致命逻辑错误:product_id定义位置错误

在productsRequest:didReceiveResponse:方法中,你把product_id的定义放在了for循环外部,此时product变量还未在循环中声明,这不仅是语法错误,还会导致所有产品都使用无效的product_id(运行时可能取到垃圾值),最终map中只会存储一个错误的条目。

2. 引用计数管理漏洞

  • 存储阶段:将新map赋值给s_map前,没有释放旧s_map中存储的SKProduct*,会导致内存泄漏——旧对象的引用计数永远无法降到0,无法被销毁。
  • 检索阶段:make_purchase中复制整个s_map完全没必要,既浪费性能又增加复杂度;另外崩溃objc_retain_x0通常是因为尝试retain一个nil或已被释放的野指针对象,大概率是之前的逻辑错误导致取到无效对象。
  • 冗余操作:[SKPayment paymentWithProduct:]返回的是autorelease对象,额外调用retain会造成内存泄漏。
  • 语法错误:if(it == map.end())中的it未定义,应该是product_it。

修正后的代码示例

存储SKProduct的修正代码

// Assume s_map is always accessed in a thread safe way    
static auto s_map = std::map<std::string, SKProduct*>{};

-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
  auto map = std::map<std::string, SKProduct*>{};
  for(SKProduct* product in response.products) {
    if(product != nil) {
      // 将product_id移到循环内部,获取当前产品的标识符
      const auto product_id = std::string([product.productIdentifier UTF8String]);
      map[product_id] = product;
      [product retain]; // 直接retain当前产品,确保map持有引用
    }
  }

  // 释放旧map中的所有对象,避免内存泄漏
  for (auto& entry : s_map) {
    [entry.second release];
  }
  s_map.swap(map); // 使用swap避免额外拷贝,提升性能
}

购买逻辑的修正代码

auto make_purchase(const product_id_t& product_id) -> void {
  const auto product_it = s_map.find(product_id);
  if(product_it == s_map.end()) {
    return;
  }

  SKProduct* product = product_it->second;
  // s_map已经持有product的引用,无需额外retain
  SKPayment* payment = [SKPayment paymentWithProduct: product];
  // payment是autorelease对象,无需手动retain

  // 继续执行购买流程...
  // 若后续需长时间持有product/payment,再调用retain,并在使用完成后release
}

关键引用计数规则总结

  • 当需要持有对象(如存入容器)时,调用retain,引用计数+1;
  • 当不再需要持有对象(如容器销毁、移除条目)时,调用release,引用计数-1;
  • 引用计数降到0时,对象会自动销毁;
  • 禁止retain nil对象,这会触发objc_retain_x0崩溃;
  • 工厂方法返回的autorelease对象,无需手动retain,除非需要延长其生命周期。

内容的提问来源于stack exchange,提问作者Viktor Sehr

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 04:15:17