使用ARC延迟释放包含的对象



我最近将我的游戏迁移到了ARC。首先,我注意到我的应用程序在玩了一段时间后崩溃了。所以我开始调试它,并注意到,在收到内存警告时,一些资源的释放被破坏了。

背景(如果您理解下面的代码,请不要阅读)

在我的游戏中,OpenGL纹理被封装在Objective-C类(Texture)中。该类跟踪"使用计数",即在给定时间有多少对象引用纹理(类似于Obj-C retainCount),其属性名为useCount。解除分配时,OpenGL纹理将被破坏。

精灵是使用TextureAtlas对象创建的。每个纹理图谱与Texture对象和纹理图像内的命名子区域的数据库相关联。在创建图集时,它将相关的Texture的使用计数增加1。此外,每个纹理图谱都会跟踪有多少Sprite实例正在引用其纹理(即,有多少精灵已经从图谱中创建并且仍然存在)。不用说,在解除分配时,每个图集都会将关联纹理的使用次数减少1。

此外,当创建新的Sprite(从TextureAtlas或其他)时,对应纹理的useCount也增加,每个子画面增加一次。并且在精灵解除分配时再次减少。

因此,只要某些Sprite引用了TextureAtlas的纹理,就无法清除图集。只要某些SpriteTextureAtlas引用了Texture,纹理也无法清除。

Texture对象和TextureAtlas对象分别由单线态TextureManagerTextureAtlasManager管理。这两个管理器负责根据需要创建资源,并在内存不足的情况下清除未使用的资源。

我选择这种设计(去耦Texture按精灵和图集使用计数,TextureAtlas按精灵使用计数),因为有时我可能需要一个纹理来处理精灵以外的东西(比如3D对象)。

还在这里吗

因此,当我收到内存警告时,首先我调用TextureAtlasMananger:中的-purge方法

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

我为保存计划删除的图集而创建的临时数组(我不喜欢从正在迭代的数组中删除对象)似乎稍后会被"自动释放"。

我检查过这个类似的问题:对象释放与ARC不一致,但我不明白它如何适用于我的情况。有问题的数组是该方法的局部变量,使用alloc/init创建。如何确保它不自动发布?(如果是这样的话)。

编辑(已解决?)

我可以确认延迟的解除分配消失了(即,代码按预期工作)如果我更换这个:

[atlasRank removeObjectsInArray:atlasesToRemove];

这个:

[atlasRank removeObjectsInArray:atlasesToRemove];
atlasesToRemove = nil;

您可以检查是否有任何对象被自动释放池无意中保留:只需将purge方法的内容包装在@autorelease块中即可。当控制流离开池的作用域时,这将删除任何最近自动发布的对象。

编辑,回答评论:

ARC没有给出关于何时将对象添加到自动发布池的确切承诺。使用优化进行编译时,生成的代码实际上有所不同。

您可以通过在自动变量(函数范围)的声明中添加objc_precise_lifetime属性来控制它的行为。

您已经找到了答案,但需要详细说明的是:因为您在缓存中创建了对每个对象的本地引用,所以这些对象中的每个对象的生存期都至少延长到循环结束时(可能会根据ARC的判断进一步延长)。如果您将本地变量标记为__weak,ARC将跳过这一步。

相关内容

  • 没有找到相关文章

最新更新