我有一个std::map<std::string, 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*>{};
const auto product_id = std::string(
[product.productIdentifier UTF8String]
);
for(SKProduct* product in response.products) {
if(product != nil) {
map[product_id] = product;
[map[product_id] retain];
}
}
s_map = map;
}
后来(购买时)我发现SKProduct* 如下所示:
auto make_purchase(const product_id_t& product_id) -> void {
// Note that the whole map is copied
const std::map<std::string, SKProduct*> map = s_map;
const auto product_it = map.find(product_id);
if(it == map.end()) {
return;
}
// Here somewhere I get a crash objc_retain_x0
SKProduct* product = product_it->second;
[product retain];
SKPayment* payment = [SKPayment paymentWithProduct: product];
[payment retain];
// Continue the purchase from here on...
}
当我从std::map
中存储/检索SKProduct*
时,我是否做错了什么?我不熟悉引用计数的Objective-C模型。
(Note与原始代码相比,为了清楚起见,代码稍微简化了一点)
1条答案
按热度按时间bvjveswy1#
对于标题中的问题,我会将这部分封装到一个自定义容器类中,以将内存管理 Package 在其中。
这里我建议使用浅拷贝方法,因为它与可可自己的集合工作方式一致。进行深拷贝被认为是一种例外情况,需要作为单独的方法(例如
NSDictionary
的initWithDictionary:copyItems:
构造函数)然而,我个人并不认为提供的代码中存在导致应用崩溃的明显错误。您所观察到的错误通常发生在向未设置为
nil
但已释放的对象发送消息时。如果没有向函数之间的Map中的对象发送release
消息,则您的SKProduct
对象必须存活。不过,以下是一些需要考虑的事项:
make_purchase
函数是从UI线程调用的。这意味着,在委托线程中派生的对象可能会从创建它们的自动释放池中出来(然而,如果您对对象进行了retain
调用,并且在阅读/写Map时没有发生竞态条件,这应该不是问题)。[SKPayment paymentWithProduct: product];
返回一个自动释放的对象,该对象至少在当前作用域结束之前不会过期,因此不需要对它执行retain
操作。clear()
。总结一下,你的
SKProductsRequestDelegate
应该看起来像这样(这里的产品是人造的,所以我在响应中动态地做了它):这里你可以看到,Map的访问/阅读与GCD和Objective-C属性的使用是同步的,我承认,当涉及到通过值访问的C++对象时,这是非常无效的。你会想优化它,但我相信它应该不会崩溃。
**P.S.**您通常还希望同步从
pendingRequests
集合阅读/写入pendingRequests
集合,但这与问题的上下文无关,因此我省略了这一部分。您还可以考虑只通过引用获取products数组,而不使用C++对象,这样应该可以很好地工作,而且更直接: