objective-cautomatic-ref-countingnszombie

Why dose the NSString become NSZomie in this code?


I just ran into a problem.I use NSMutableArray to keep the Ad ids that need to be loaded. When one id is preloaded, I remove it from the array. Then I find out that, after remove, the unit id became a zombie.

I tried to reproduce this, and I find out that when the Ads id was pulled from array, it is not a zombie.It just become zombie after removed from array. But, there is still a NSString* refer to it, how can this happen? And If it will become zombie at this point, it should become zombie every time. But it only happens occasionally.

 -(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;
         }
     }
 }

Solution

  • It's worth taking a close look at cocoa's memory management policies. The thing here (it seems to me) is that when your code assigns the string adsName, the object the preloadNextRewardVideo method is part of doesn't take ownership of the string-object that adsName is pointing at ('ownership' in this context means either allocating and initializing space for it through alloc/init, new, copy, etc, or sending it an explicit retain message). All you have is a local variable that points at a string-object owned by the _allRewardVideoAds array. Yes, making the assignment increases the retain count, but that retain is autoreleased, and limited to the scope of this method. As soon as this method ends, nothing is going to own that string-object, and it will deallocate.

    This would not be an issue (and would not create the NSZombie flag) except that you just sent adsName to a different object (GADRewardBasedVideoAd* ads) that I'm guessing does not retain it either. Of course, ads is also autoreleased (nothing owns it outside of this method), but what is that, a race-condition over whether ads or adsName is deallocated first? I suspect you do have a race-condition because NSZombie only shows up sometimes, but I don't know enough about the internal mechanisms of autorelease to know how that might work.

    I think NSZombie is just telling you that you have an object that is:

    ARC is anticipating a problem, and using NSZombie to let you know.

    You could fix this either by using the property setter (e.g. use self.currentRewardVideoName = adsName) which retains the string-object globally for this object, or by locally copying it (NSString* adsName = [[_allRewardVideoAds objectAtIndex:0] copy]) which makes sure that your object owns the string to the end of the method.