Как предотвратить объект, создаваемый на "куче"?

Причина, по которой аудио останавливается, заключается в том, что у вас есть только один AVAudioPlayer, поэтому, когда вы попросите класс воспроизвести другой звук, вы в настоящее время заменяете старый экземпляр новым экземпляром AVAudioPlayer. Вы переписываете его в основном.

Вы можете создать два экземпляра класса GSAudio, а затем вызвать playSound для каждого из них или сделать класс универсальным аудио-менеджером, который использует словарь аудиопланеров.

I много предпочитает последний вариант, поскольку он позволяет использовать более чистый код и также более эффективен. Вы можете проверить, действительно ли вы уже сделали плеер для звука, вместо того, чтобы, например, создать нового игрока.

В любом случае я переделал ваш класс для вас, чтобы он воспроизводил несколько звуков однажды. Он также может воспроизводить один и тот же звук над собой (он не заменяет предыдущий экземпляр звука). Надеюсь, что это поможет!

Класс является одноэлементным, поэтому для доступа к классу используйте:

GSAudio.sharedInstance

, например, для воспроизведения звука, который вы вызывали:

GSAudio.sharedInstance.playSound("AudioFileName")

, и одновременно воспроизводить несколько звуков:

GSAudio.sharedInstance.playSounds("AudioFileName1", "AudioFileName2")

или вы могли бы загрузите звуки в массиве где-нибудь и вызовите функцию playSounds, которая принимает массив:

let sounds = ["AudioFileName1", "AudioFileName2"]
GSAudio.sharedInstance.playSounds(sounds)

Я также добавил функцию playSounds, которая позволяет вам откладывать каждый звук, воспроизводимый в каскадном формате. Итак:

 let soundFileNames = ["SoundFileName1", "SoundFileName2", "SoundFileName3"]
 GSAudio.sharedInstance.playSounds(soundFileNames, withDelay: 1.0)

будет воспроизводить звук2 через секунду после звука1, затем звук3 будет воспроизводить секунду после звука2 и т. Д.

Вот класс:

class GSAudio: NSObject, AVAudioPlayerDelegate {

    static let sharedInstance = GSAudio()

    private override init() {}

    var players = [NSURL:AVAudioPlayer]()
    var duplicatePlayers = [AVAudioPlayer]()

    func playSound (soundFileName: String){

        let soundFileNameURL = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource(soundFileName, ofType: "aif", inDirectory:"Sounds")!)

        if let player = players[soundFileNameURL] { //player for sound has been found

            if player.playing == false { //player is not in use, so use that one
                player.prepareToPlay()
                player.play()

            } else { // player is in use, create a new, duplicate, player and use that instead

                let duplicatePlayer = try! AVAudioPlayer(contentsOfURL: soundFileNameURL)
                //use 'try!' because we know the URL worked before.

                duplicatePlayer.delegate = self
                //assign delegate for duplicatePlayer so delegate can remove the duplicate once it's stopped playing

                duplicatePlayers.append(duplicatePlayer)
                //add duplicate to array so it doesn't get removed from memory before finishing

                duplicatePlayer.prepareToPlay()
                duplicatePlayer.play()

            }
        } else { //player has not been found, create a new player with the URL if possible
            do{
                let player = try AVAudioPlayer(contentsOfURL: soundFileNameURL)
                players[soundFileNameURL] = player
                player.prepareToPlay()
                player.play()
            } catch {
                print("Could not play sound file!")
            }
        }
    }


    func playSounds(soundFileNames: [String]){

        for soundFileName in soundFileNames {
            playSound(soundFileName)
        }
    }

    func playSounds(soundFileNames: String...){
        for soundFileName in soundFileNames {
            playSound(soundFileName)
        }
    }

    func playSounds(soundFileNames: [String], withDelay: Double) { //withDelay is in seconds
        for (index, soundFileName) in soundFileNames.enumerate() {
            let delay = withDelay*Double(index)
            let _ = NSTimer.scheduledTimerWithTimeInterval(delay, target: self, selector: #selector(playSoundNotification(_:)), userInfo: ["fileName":soundFileName], repeats: false)
        }
    }

     func playSoundNotification(notification: NSNotification) {
        if let soundFileName = notification.userInfo?["fileName"] as? String {
             playSound(soundFileName)
         }
     }

     func audioPlayerDidFinishPlaying(player: AVAudioPlayer, successfully flag: Bool) {
        duplicatePlayers.removeAtIndex(duplicatePlayers.indexOf(player)!)
        //Remove the duplicate player once it is done
    }

}
26
задан SCFrench 15 June 2016 в 02:16
поделиться

7 ответов

ответ Nick's является хорошей начальной точкой, но неполный, поскольку на самом деле необходимо перегрузиться:

private:
    void* operator new(size_t);          // standard new
    void* operator new(size_t, void*);   // placement new
    void* operator new[](size_t);        // array new
    void* operator new[](size_t, void*); // placement array new

(Хорошая практика кодирования предложила бы, необходимо также перегрузить удаление и удалить [] операторы - я был бы, но так как они не собираются называться, это не действительно необходимо.)

Pauldoo также корректен, что это не переживает агрегацию на Foo, хотя это действительно переживает наследование от Foo. Вы могли сделать некоторое шаблонное волшебство метапрограммирования ПОМОЧЬ предотвратить это, но это не будет неуязвимо для "злых пользователей" и таким образом вероятно, не стоит сложности. Документация того, как это должно использоваться, и обзор кода для обеспечения его, используется правильно, путь на только ~100%.

25
ответ дан Community 28 November 2019 в 07:16
поделиться

Я не знаю, как сделать это надежно и портативным способом.. но..

, Если объект находится на стеке тогда, Вы могли бы быть в состоянии утверждать в конструкторе, что значение 'этого' всегда близко к указателю вершины стека. Существует хороший шанс, что объект будет на стеке, если это верно.

я полагаю, что не все платформы реализуют свои стеки в том же направлении, таким образом, Вы могли бы хотеть сделать одноразовый тест, когда приложение начинает проверять, какой путь стек выращивает.. Или сделайте некоторую выдумку:

FooClass::FooClass() {
    char dummy;
    ptrdiff_t displacement = &dummy - reinterpret_cast<char*>(this);
    if (displacement > 10000 || displacement < -10000) {
        throw "Not on the stack - maybe..";
    }
}
7
ответ дан pauldoo 28 November 2019 в 07:16
поделиться

Вы могли перегрузить новый для Foo и сделать его частным. Это означало бы, что компилятор будет стонать..., если Вы не создадите экземпляр Foo на "куче" из Foo. Для ловли этого случая Вы не могли просто записать новый метод Foo, и затем компоновщик будет стонать о неопределенных символах.

class Foo {
private:
  void* operator new(size_t size);
};

пз. Да, я знаю, что это может обойтись легко. Я действительно не рекомендую его - я думаю, что это - плохая идея - я просто отвечал на вопрос!;-)

9
ответ дан Nick 28 November 2019 в 07:16
поделиться

@Nick

Это могло обойтись путем создания класса, который происходит из или агрегировал Foo. Я думаю, что я предлагаю (в то время как не устойчивый), все еще работал бы на полученные и агрегирующиеся классы.

, Например:

struct MyStruct {
    Foo m_foo;
};

MyStruct* p = new MyStruct();

Здесь я создал экземпляр 'Foo' на "куче", обойдя скрытый новый оператор Foo.

3
ответ дан pauldoo 28 November 2019 в 07:16
поделиться

Вы могли объявить функцию, вызванную "оператор, новый" в классе Foo, который блокирует доступ к нормальной форме новых.

действительно ли это - вид поведения, которое Вы хотите?

0
ответ дан David 28 November 2019 в 07:16
поделиться

Вы могли объявить его как интерфейс и управлять классом реализации более непосредственно от Вашего собственного кода.

0
ответ дан angry person 28 November 2019 в 07:16
поделиться

Не уверенный, если это предлагает какие-либо возможности времени компиляции, но Вы посмотрели на перегрузку 'нового' оператора для Вашего класса?

-1
ответ дан Will Dean 28 November 2019 в 07:16
поделиться
Другие вопросы по тегам:

Похожие вопросы: