Note that there are some explanatory texts on larger screens.

plurals
  1. PODelayed deallocation of contained objects with ARC?
    primarykey
    data
    text
    <p>I recently migrated my game to ARC. First, I noticed my app crashed after playing for a while. So I began debugging it and noticed that, on receiving a memory warning, the deallocation of some resources was being corrupted.</p> <p><strong>Background</strong> (Don't read if you understand the code below)</p> <p>In my game, OpenGL textures are encapsulated in an Objective-C class (<code>Texture</code>). This class keeps track of the 'use count', i.e. how many objects are referencing the texture at a given time (similar to Obj-C <code>retainCount</code>) with a property adeptly named <code>useCount</code>. On deallocation, the OpenGL texture is destroyed.</p> <p>Sprites are created using <code>TextureAtlas</code> objects. Each texture atlas is associated with a <code>Texture</code> object and a database of named subregions inside the texture image. On creation of the atlas, it increases the associated <code>Texture</code>'s use count by 1. Also, each texture atlas keeps track of how many <code>Sprite</code> instances are referencing its texture (i.e., how many sprites have been created from the atlas and are still around). Needless to say, on deallocation each atlas decreases the associated texture's use count by 1.</p> <p>In addition, when a new <code>Sprite</code> is created (from a <code>TextureAtlas</code> or otherwise), the <code>useCount</code> of the corresponding texture is also increased, once by each sprite. And decreased again, on sprite deallocation.</p> <p>So, as long as some <code>Sprite</code> is referencing a <code>TextureAtlas</code>'s texture, the atlas can't be purged. And as long as some <code>Sprite</code> or <code>TextureAtlas</code> is referencing a <code>Texture</code>, the texture can't be purged either.</p> <p><code>Texture</code> objects and <code>TextureAtlas</code> objects, in turn, are managed by the singletons <code>TextureManager</code> and <code>TextureAtlasManager</code> respectively. These two managers are responsible for creating resources as necessary and purging unused resources on low-memory situations.</p> <p>I chose this design (decoupling <code>Texture</code> use count by sprites and atlases, and <code>TextureAtlas</code> use count by sprites) because some times I might need a texture for something else than a sprite (say, a 3D object).</p> <p><strong>Still here?</strong></p> <p>So, when I receive a memory warning, first I call the <code>-purge</code> method in the <code>TextureAtlasMananger</code> :</p> <pre><code>- (void) purge { // Called on Low Memory Situations. // purges unused atlases. // _atlasRank is an array of atlases in MRU order // _atlasDatabase is a dictionary of atlases keyed by their name NSUInteger count = [_atlasRank count]; NSMutableArray* atlasesToRemove = [[NSMutableArray alloc] init]; for (NSUInteger i=0; i &lt; count; i++) { TextureAtlas* atlas = [atlasRank objectAtIndex:i]; if ([atlas canDelete]) { // Means there are no sprites alive that where created // from this atlas [atlasesToRemove addObject:atlas]; [_atlasDatabase removeObjectForKey:[atlas name]]; NSLog(@"TextureAtlasManager: Successfully purged atlas [%@]", [atlas name]); } else{ // Means some sprite remains that was // created from this atlas NSLog(@"TextureAtlasManager: Failed to purge atlas [%@]", [atlas name]); } } [_atlasRank removeObjectsInArray:atlasesToRemove]; // At this point, atlasesToRemove should be deallocated // by ARC, and every atlas in atlasesToRemove // should be deallocated as well. // This FAILS to delete unused textures: [[TextureManager sharedManager] purgeUnusedTextures]; // (:Removed atlases are not yet deallocated and 'retain' // their texture) // ...But This SUCCEEDS: [[TextureManager sharedManager] performSelector:@selector(purgeUnusedTextures) withObject:nil afterDelay:0.5]; // (i.e., -[TextureAtlas dealloc] gets called before -purgeUnusedTextures) } </code></pre> <p>It seems that the temporary array I created to hold the atlases scheduled for deletion (I don't like removing objects from the array being iterated) is being 'autoreleased' later.</p> <p>I've checked this similar question: <a href="https://stackoverflow.com/questions/9173120/inconsistent-object-deallocation-with-arc">Inconsistent object deallocation with ARC?</a> , but I fail to see how it applies to my case. The array in question is a local variable of the method, created with alloc/init. How can I ensure it is not autoreleased? (if that is the case).</p> <p><strong>EDIT</strong> (Solved?)</p> <p>I can confirm that the delayed deallocation disappears (i.e., code works as intended) if I replace this:</p> <pre><code>[atlasRank removeObjectsInArray:atlasesToRemove]; </code></pre> <p>with this:</p> <pre><code>[atlasRank removeObjectsInArray:atlasesToRemove]; atlasesToRemove = nil; </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload