【问题标题】:Why dose the NSString become NSZomie in this code?为什么 NSString 在这段代码中会变成 Zombies?
【发布时间】:2019-07-07 09:50:53
【问题描述】:

我刚刚遇到了一个问题。我使用 NSMutableArray 来保留需要加载的广告 ID。当一个 id 被预加载时,我将它从数组中删除。然后我发现,删除后,单位id变成了僵尸。

我试图重现这一点,我发现当从数组中提取广告 id 时,它不是僵尸。它只是从数组中删除后变成了僵尸。但是,还有一个 NSString* 引用它,怎么会这样呢?而如果它此时会变成僵尸,那么它应该每次都变成僵尸。但它只是偶尔发生。

 -(void)preloadNextRewardVideo
 {
     if([_allRewardVideoAds count])
     {
         NSString* adsName = [_allRewardVideoAds objectAtIndex:0];  //the element is not a zombie here
         GADRewardBasedVideoAd* ads = [self ensureRewardVideo:adsName];
         if(![ads isReady])
         {
             _currentRewardVideoName = adsName;
             [_allRewardVideoAds removeObjectAtIndex:0];
             GADRequest *request = [GADRequest request];
             [ads loadRequest:request withAdUnitID:adsName];  //here, adsName is a zombie
             _isRewarLoading = YES;
         }
     }
 }

【问题讨论】:

  • 你有 ARC 吗?
  • @bbum 不,我没有开启 ARC。

标签: objective-c automatic-ref-counting nszombie


【解决方案1】:

值得仔细看看可可的memory management policies。这里的事情(在我看来)是,当您的代码分配字符串 adsName 时,preloadNextRewardVideo 方法所属的对象不会获得adsName 指向的字符串对象的所有权('所有权'在这种情况下,意味着通过 alloc/init、new、copy 等为其分配和初始化空间,或者向其发送显式保留消息)。您所拥有的只是一个指向_allRewardVideoAds 数组所拥有的字符串对象的局部变量。是的,进行分配会增加保留计数,但保留是自动释放的,并且仅限于此方法的范围。一旦这个方法结束,没有任何东西会拥有那个字符串对象,它会被释放。

这不会是一个问题(并且不会创建 NSZombie 标志),除非您只是将adsName 发送到另一个对象(GADRewardBasedVideoAd* ads),我猜它也不会保留它。当然,ads 也是自动释放的(在此方法之外没有人拥有它),但那是什么,是先释放ads 还是adsName 的竞争条件?我怀疑你确实有竞争条件,因为 NSZombie 有时只出现,但我对自动释放的内部机制知之甚少,不知道它是如何工作的。

我认为 NSZombie 只是在告诉你,你有一个对象是:

  • 被外部对象使用,并且
  • 由于自动释放很快就会被销毁。

ARC 预计会出现问题,并使用 NSZombie 通知您。

你可以通过使用属性设置器(例如使用self.currentRewardVideoName = adsName)来解决这个问题,它为这个对象全局保留字符串对象,或者通过本地复制它(NSString* adsName = [[_allRewardVideoAds objectAtIndex:0] copy])来确保你的对象拥有字符串到方法结束。

【讨论】:

  • 感谢您的回答。您的意思是分配给本地指针会隐式执行保留和自动释放吗?连ARC都不上?因为我这里没有使用 ARC。
  • 抱歉,我只是假设您使用的是 ARC。唯一真正的区别是 ARC 在幕后做了很多保留/释放簿记。但要点仍然存在:从数组中删除 adsName 后,没有对象拥有它。如果 ARC 开启,当您将字符串分配给 adsName 时,您将获得自动保留/自动释放,但 ARC 已关闭并且您不会显式发送变量保留/自动释放,因此一旦从数组(从集合中出来的对象被释放,而不是自动释放)它的保留计数下降到零。
  • 这是令人困惑的部分,如果在非ARC模式下不会自动调用retain,我应该总是遇到这个问题。为什么它只是偶尔发生?
  • 开启 ARC 会解决我的问题吗?如果我打开 ARC,因为我在写这篇文章时从未想过 ARC,我的代码会不会有一些潜在的内存问题?
  • 我认为你只是偶尔遇到这个问题,因为释放和释放不一定有严格的顺序;如果两个对象在释放队列中,您无法确定哪个会先死,如果存在依赖关系,只有当错误的对象先死时才会出现问题。启用 ARC 将意味着大量的重写和重构,因为 ARC 具有不同的设计模式。我建议您在迈出这一步之前先阅读它。
【解决方案2】:

由于您没有启用 ARC,这意味着您需要自己手动管理内存。由于您没有启用 ARC,这意味着您需要自己手动管理内存。尽管大多数 API 会从方法返回一个自动释放的对象,但情况并非总是如此。

对于这种情况,NSArray 保留了自身包含的对象,因此它可能不需要从 objectAtIndex: 方法返回自动释放的对象,因为它应该自己保留作为优化。在这种情况下,我建议您在从数组中获取对象时调用保留对象并在此方法结束之前释放它。这将有助于这种情况。

【讨论】:

  • 谢谢,我已经为文件打开了 ARC,现在可以正常工作了。很遗憾,我不能将超过 1 个答案标记为“正确”,因为您也很有帮助。
  • 没关系。很高兴我的回答能帮到你。请标记更适合您需要的答案:)
猜你喜欢
  • 2013-04-25
  • 1970-01-01
  • 2018-10-28
  • 2013-04-27
  • 2012-06-06
  • 2018-08-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多