Ios SpriteKit声音内存消耗问题

Ios SpriteKit声音内存消耗问题,ios,objective-c,macos,sprite-kit,Ios,Objective C,Macos,Sprite Kit,为了进一步熟悉SpriteKit,我制作了一个简单的小应用程序,可以在你点击或触摸的任何位置生成一个随机(3个选项)球(7秒后自动消失),然后应用物理和重力,并以设定的间隔操纵重力结束。这很简单,而且效果很好。然而,我决定给它添加声音,这样球在撞击墙壁或彼此时会发出噪音 虽然它起作用了,但它提出了一个我自己无法解决的问题:如果你产生了太多的球,它无法处理,那么无声版本会滞后,但此后声音版本继续滞后。我查看了Xcode可靠的调试导航器,发现应用程序的内存随着每个球添加到场景中而不断扩展。无声版本并

为了进一步熟悉SpriteKit,我制作了一个简单的小应用程序,可以在你点击或触摸的任何位置生成一个随机(3个选项)球(7秒后自动消失),然后应用物理和重力,并以设定的间隔操纵重力结束。这很简单,而且效果很好。然而,我决定给它添加声音,这样球在撞击墙壁或彼此时会发出噪音

虽然它起作用了,但它提出了一个我自己无法解决的问题:如果你产生了太多的球,它无法处理,那么无声版本会滞后,但此后声音版本继续滞后。我查看了Xcode可靠的调试导航器,发现应用程序的内存随着每个球添加到场景中而不断扩展。无声版本并没有在内存上扩展太多,而且在从父母手中取出球后,还回收了一些比特

我忍不住想,我的实现中出现了一些错误,这不仅是在使用完声音文件后将其保留在内存中,而且还保留了每个文件的倍数

我很乐意与任何提出请求的人分享我的项目,但这是发生的方法的近似值:

初始化时,声音动作将创建并存储在场景类中,以便以后可以访问:

@interface REP_Balls () {
    SKAction* ballSound01;
}
@end

@implementation REP_Balls {
...
...
...
-(void) setUpSounds {
    NSArray* array = @[ @"ball_hit_01.wav",]; //I have more in the array, I'm just simplifying code for stackoverflow

    ballSound01 = [SKAction playSoundFileNamed:[array objectAtIndex:0] waitForCompletion:NO];

}
然后,我让一个联系人侦听器激活一个方法,该方法根据可用的声音动作进行随机化,并返回其中一个动作:

-(void) didBeginContact:(SKPhysicsContact *)contact {
SKPhysicsBody *firstBody, *secondBody;

firstBody = contact.bodyA;
secondBody = contact.bodyB;

SKAction* randomSound;
//blah blah blah if categories match and whatnot, do this:
        REP_BallSpawn* ball = (REP_BallSpawn*) firstBody.node;
        randomSound = [self soundBankRandomizer:YES];

        [ball runAction:randomSound];
以及随机化的方法:

-(SKAction*) soundBankRandomizer:(BOOL)isWallHit {
NSArray* array;
switch (isWallHit) {
    case YES:
        array = @[ //according sound actions
              ];
        break;

    case NO:
        array = @[
                ballSound01, //and other sound actions
                  ];

    default:
        array = @[
                  //if neither case is true somehow, just choose from a bank of all sound actions
                  ];
        break;
}

int randomChoice = arc4random() % [array count];    
SKAction* sound = (SKAction*)[array objectAtIndex:randomChoice];
return sound;
}
除此之外,7秒钟后,球会自动脱落,我会假设有任何孩子(我也假设应该包括动作,比如声音动作)

我甚至制定了一种方法来确认在节点解体后没有节点粘住:

[self enumerateChildNodesWithName:@"//*" usingBlock:^(SKNode *node, BOOL *stop) {
        string = [NSString stringWithFormat:@"%@  (())(())  %@", string, node];
    }];
然后将结果复制到剪贴板,从而生成场景中当前所有节点和子节点的列表。(其中的“(())(())”用于在“查找/替换”中搜索唯一的模式以生成新行)

编辑:我还注意到,CPU使用率仍然很高后,一切都已绝望以及

请告诉我我做错了什么

===================================================

所以我对代码做了一些修改。正如下面提到的,我让它正常工作,但经过进一步的测试,它似乎对如何实现它很挑剔。也许其他人会明白原因,但这对我来说毫无意义

有效的方法

与上述相同,但有以下更改:

-(void) setUpSounds {

NSArray* array = @[
                  @"ball_hit_01.m4a",
                  @"ball_hit_02.m4a",
                  @"ball_hit_03.m4a",
                  @"ball_hit_04.m4a",
                  @"wall_hit_01.m4a",
                  @"wall_hit_02.m4a",
                  @"floor_hit_01.m4a",
                  @"floor_hit_02.m4a",
                  ];


SKAction* ballSound01 = [SKAction playSoundFileNamed:[array objectAtIndex:0] waitForCompletion:NO];
    // etc


sounds = [NSArray arrayWithObjects:ballSound01, ballSound02, ballSound03, ballSound04, wallSound01, wallSound02, floorSound01, floorSound02, nil]; //is declared class wide

}



 -(SKAction*) soundBankRandomizer:(BOOL)isWallHit {

int randomChoice = arc4random() % ([sounds count] / 2);

if (isWallHit) {
    randomChoice += ([sounds count] / 2);
}

return [sounds objectAtIndex:randomChoice];
}
contact listener几乎完全相同(我删除了球的铸造,并将动作应用于世界节点)。我也试过用字典代替数组,效果很好

由于某种原因不起作用的方法

我将删除soundBankRandomizer方法,以及从联系人侦听器中删除任何操作调用。相反,我添加了以下方法:

-(void)playSound:(BOOL)isWallHit {

int randomChoice = arc4random() % ([sounds count] / 2);

if (isWallHit) {
    randomChoice += ([sounds count] / 2);
}

SKAction* randomSound = [sounds objectAtIndex:randomChoice];

[bgNull runAction:randomSound];

}
然后通过以下方式从联系人侦听器调用它:

[self playSound:YES]; //for wall hit
[self playSound:NO]; //for ball on ball action

出于某种原因,这导致了与我最初遇到的完全相同的性能问题。这对任何人都有意义吗

我发现了几个问题……通过声明SKAction ballSound01,它将继续使用。我也遵循您的想法,通过删除每个节点,它将删除每个操作,但我再次认为,通过声明它,这不会发生。此外,SkActs可以应用于多个节点,所以我认为您不能真正把它们看作是它们运行的对象的子集。 我认为您最好声明一个包含这些操作的数组,而不是每次在soundBankRandomizer中创建它

此外,我还从未见过一个switch语句包含这样的bool变量。就这么做

 if (isWallHit == YES) {

 } else{

 }
更重要的是,默认块无论如何都不会运行。因为isWallHit要么是,要么不是,这两个条件就是它,不需要默认值

您可能想考虑将单级类作为一个声音管理器,并将其与节点完全分开。因此,与其打电话

randomSound = [self soundBankRandomizer:YES];  
[ball runAction:randomSound];
你可以称之为

[[SoundManager sharedSounds] playRandomWallSound];

无需在播放声音时制作特定的节点部分

我想出了一个解决办法!首先,我按照您的建议声明了一个数组,而不是每次都将每个操作复制到一个数组中。虽然这并不能从整体上解决问题,但我认为这不仅是更好的练习,而且还节省了一些内存。但是,受你的建议的启发,让一个单例管理声音(从长远来看,这似乎是一个更好的解决方案),我将所有声音动作应用到背景空对象(myWorld或其他)。现在可以了!内存和cpu使用率恢复到正常水平后,一切都消失了,虽然!这才是最重要的!