AS3, передавая функцию в качестве параметра, создает утечки памяти - PullRequest
8 голосов
/ 06 сентября 2011

У меня есть функция, которая принимает другую функцию в качестве параметра.Примерно так:

public function onHits(target : Shape, callback : Function) : void

Я использую его, передавая функцию-член в качестве параметра, который должен вызываться всякий раз, когда переданная цель достигает чего-то.Функция вызывается много раз в кадре.Так что он используется:

//code...
CollisionManager.onHits(myShape, onHitCB);
//code...

Функция при попадании:

public function onHitCB(hitObject : *) : void 
{
    //removed all code to test this problem
}

Когда я это делаю, у меня возникает утечка памяти.Я выделил проблему этому методу onHits и прокомментировал все остальное.onHits - пустой метод без кода внутри, onHitCB также пуст.Если я закомментирую вызов onHits, утечки памяти не будет, и если я передам null вместо onHitCB, утечки памяти не будет.

Так что, очевидно, когда я передаю HITCB в качестве параметра, это проблема.Поэтому я подумал, что это может быть потому, что Flash выделяет некоторую память для создания указателя на функцию и не освобождает ее, но я вызываю System.gc () каждый кадр в режиме отладки, и утечка все еще присутствует.Это означало бы, что это либо ошибка в SDK, либо я что-то не так делаю.

Я нашел странный обходной путь, сохранив переменную, которая указывает на функцию, которую я назначаю в конструкторе моегообъект:

private var func : Function;

public function MyObject() 
{
    func = onHitCB;
}

, и это устранит утечку памяти , даже если я все еще передаю значение HitCB в качестве параметра .Так что это будет означать, что это не функция «getter» для получения onHitCB, а что-то еще, вызывающее утечку памяти?

Я очень запутался.Как это может вызвать утечку памяти:

public function MyObject() 
{
}

public function update() : void
{
    CollisionManager.onHits(myShape, onHitCB);//empty function
}

public function onHitCB(hitObject : *) : void 
{
    //removed all code to test this problem
}

, но не это?:

private var func : Function;
public function MyObject() 
{
    func = onHitCB;
}

public function update() : void
{
    CollisionManager.onHits(myShape, onHitCB);//empty function
}

public function onHitCB(hitObject : *) : void 
{
    //removed all code to test this problem
}

и есть ли способ не делать этот обходной путь?

Ответы [ 4 ]

5 голосов
/ 06 сентября 2011

[...] связанные методы создаются автоматически при передаче метода в качестве параметра. Связанные методы гарантируют, что ключевое слово this всегда ссылается на объект или класс, в котором определен метод. Источник

Звучит так, будто создание ссылки на метод не использует простой метод получения. Новый объект замыкания метода - сгенерированный . Итак, ваше предположение верно.

Интересно, почему ссылки не кэшируются для каждого экземпляра и почему они не являются сборщиком мусора. Лучше избегать создания нескольких ссылок. Ссылка на метод только один раз - это именно то, что я буду делать, когда мне придется использовать этот метод в нескольких местах, поэтому большую часть времени я бы не назвал это обходным путем, а хорошей практикой СУХОЙ. В вашем примере это, конечно, имеет смысл, если предположить, что для ссылки на метод используется простой метод получения.

1 голос
/ 06 сентября 2011

Для получения дополнительной информации о том, что именно вызывает и не вызывает утечки памяти при использовании функциональных приемов, ознакомьтесь с http://www.developria.com/2010/12/functional-actionscript-part-1.html.Кроме того, имейте в виду, что использование статических методов, подобных этому, является очень плохой практикой (http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/),), и вы только начинаете сталкиваться со многими проблемами, вызванными использованием этой техники. Похоже, вы достаточно рано в своем проекте, что выВы не совсем привержены этому пути, поэтому вам может понадобиться поискать другие способы его программирования.

0 голосов
/ 08 сентября 2011

И вот почему мы не делаем такого рода вещи в программировании в стиле ООП.Лучше всего сделать это правильно и добавить обратный вызов в класс CollisionManager.Причина, по которой это может быть GCed, когда вы сохраняете локальную ссылку, заключается в том, что функция никогда не теряет область видимости, потому что эта переменная там содержит ссылку.Как только что-то теряет сферу действия, становится почти невозможно собрать его.Попробуйте это и посмотрите, как вы теряете прицел.

private var somevar:String = 'somevar with a string';
public function MyObject() 
{
}

public function update() : void
{
    CollisionManager.onHits(myShape, onHitCB);//empty function
}

public function onHitCB(hitObject : *) : void 
{
    trace(this.somevar) // scope should be lost at this point and somevar should be null or toss an error.
}
0 голосов
/ 06 сентября 2011

Я не уверен, что ваш код в функции onHits, но если для этого не требуется дополнительное время, чтобы закончить в другом цикле.тогда я рекомендую вам сделать так:

static public function onHits(target : Shape) : *
{
    // do what you need

    // return the hitObject;
    return hitObject;
}

и

public function update() : void
{
    // parse the object direc to to the function.
    onHitCB ( CollisionManager.onHits(myShape) );
}

public function onHitCB(hitObject : *) : void 
{
    if ( hitObject == null )
        return;

    // if it not null then do all your calculations.
    //removed all code to test this problem
}
...