Руководство по actionscript. часть 2, стр. 093

}

Неправильное размещение блока finally вызовет ошибку на этапе компиляции. В предыдущем коде блок finally будет выполнен сразу после того, как:

? выполнение блока try завершится без ошибок;

? блок catch обработает исключение, сгенерированное блоком try;

? необработанное исключение поднимется вверх по иерархии объектов;

? оператор return, continue или break передаст управление программой за пределы блоков try или catch.

Блок final 1у инструкции try/ cat ch/final 1у обычно содержит очищающий код, который должен выполняться независимо от того, возникло исключение в соответ-

ствующем блоке try или нет. Предположим, что мы создаем игру в жанре «космический шутер» и определяем класс Spaceship, представляющий космические корабли. У класса Spaceship есть метод attackEnemy ( ), который выполняет следующее.

? Устанавливает текущую цель для космического корабля.

? Стреляет по выбранной цели.

? Удаляет выбранную цель (присваивая переменной currentTarget объекта Spaceship значение null).

Предположим, что в нашем гипотетическом приложении при выполнении первых двух из описанных задач может возникнуть исключение. Более того, предположим, что метод attackEnemy ( ) не обрабатывает эти исключения самостоятельно; он передает исключения вызывающему методу. Независимо от того, было сгенерировано исключение или нет, метод attackEnemy ( ) должен присвоить переменной currentTarget значение null.

Вот так выглядел бы метод attackEnemy ( ), если бы мы запрограммировали его с помощью оператора catch (то есть без использования блока finally):

public function attackEnemy (enemy:SpaceShip):void { try {

setCurrentTarget(enemy): fireOnCurrentTargetC ): } catch (e:Error) { // Удаляем текущую цель, если возникло исключение. setCurrentTarget(nul1): // Передаем исключение вызывающему методу, throw е:

}

// Удаляем текущую цель, если никаких исключений не возникло. setCurrentTarget(nul1);

}

Здесь мы вынуждены дублировать инструкцию setCurrentTarget (null). Мы поместили ее и внутрь блока catch, и после инструкции try/catch, гарантируя тем самым, что она будет выполнена независимо от того, возникло исключение в блоке try или нет. Тем не менее дублирование инструкции может привести к ошибке. В предыдущем методе программист мог бы легко забыть удалить текущую цель после блока try/catch.

Руководство по actionscript. часть 2, стр. 094

Если изменить нашу стратегию так, чтобы текущая цель удалялась в блоке fina 11 у, мы устраним ненужные команды в предыдущем коде:

public function attackEnemy (enemy:SpaceShiр):void { try {

setCurrentTarget(enemy): fireOnCurrentTarget( ): } finally { setCurrentTarget(nul1);

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

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

// Этот код выглядит подходящим, однако в нем существует проблема. // Сможете определить ее?

public function attackEnemy (enemy:SpaceShiр):void { setCurrentTarget(enemy); fireOnCurrentTarget( ); setCurrentTarget(nul1);

}

Запомните, что, когда генерируется исключение, управление программой передается в ближайший подходящий блок catch в стеке вызовов. Следовательно, если метод fir eOnCur rent Target ( ) генерирует исключение, управление передается в метод attackEnemy ( ), при этом обратно в метод fireOnCurrentTarget ( ) управление возвращено не будет и инструкция setCurrentTarget (null) останется невыполненной. Однако с помощью блока finally мы гарантируем, что инструкция setCurrentTarget (null) будет выполнена до того, как исключение поднимется вверх по иерархии объектов.

Пример метода attackEnemy ( ) отражает наиболее распространенное использование блока finally в многопоточных приложениях, в которых одновременно может выполняться сразу несколько фрагментов кода и которые разрабатываются с помощью таких языков программирования, как, например, Java. В языке Java следующая общая структура — обычное явление; она исключает возможность удаления объекта, выполняющего некую задачу, другим объектом в процессе выполнения текущей задачи:

// Установить состояние, обозначающее выполнение данным объектом текущей // задачи. Внешние объекты должны проверять состояние данного объекта перед // тем, как обратиться к нему или выполнить над ним какие-либо действия. doingSomething = true: try {

// Выполняем задачу. doSomething( ); } finally {

// Сбросить состояние, обозначающее выполнение текущей задачи (независимо // от того, возникло в процессе выполнения задачи исключение или нет). doingSomething = false:

}

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

можно использовать для организационных целей — помещать в него код, который выполняет очистку после выполнения другого кода.

Руководство по actionscript. часть 2, стр. 095

Вложенные исключения

До сих пор мы использовали только одноуровневые инструкции try/catch/ finally, однако логика обработки исключений может быть и вложенной. Инструкция try/catch/finally может размещаться внутри блока try, catch или finally другой инструкции try/catch/finally. Такое иерархическое вложение позволяет любому блоку инструкции try/catch/finally выполнять код, который, в свою очередь, может генерировать исключения.

Предположим, что мы создаем многопользовательское веб-приложение, представляющее доску объявлений. Мы определяем следующие классы: BulletinBoard — основной класс приложения, GUIManager — класс, управляющий пользовательским интерфейсом, и User — класс, который представляет пользователя на доске. В классе BulletinBoard мы описываем метод populateUserList ( ), который отображает список активных пользователей на текущий момент. Выполнение мето-дapopulateUserList ( ) состоит из двух этапов: на первом этапе метод получает экземпляр класса List из экземпляра класса GUIManager нашего приложения. Класс List представляет отображаемый на экране список пользователей. Затем метод populateUserList ( ) заполняет экземпляр класса List пользователями из переданного массива экземпляров класса User. На обоих этапах существует потенциальная возможность возникновения исключения, поэтому в методе populateUserList ( ) используется вложенная структура try/catch/finally. Рассмотрим эту вложенную структуру поближе.

EcлинaпepвoмэтaпeвыпoлнeниямeтoдapopulateUserList ( ) экземпляр класса List окажется недоступным, будет сгенерировано исключение UserLis tNotFound экземпляром класса GUIManager. Исключение UserLi s tNotFound обрабатывается внешней инструкцией try/catch/finally.

Если, с другой стороны, экземпляр класса List окажется доступным, метод populateUserList ( ) перейдет к выполнению второго этапа, где с помощью цикла заполнит экземпляр класса List пользователями из переданного массива. На каждой итерации цикла, если ID текущего пользователя не может быть найдено, метод User. gelD ( ) генерирует исключение UserldNotSet. Оно обрабатывается вложенной инструкцией try/catch/finally.

Рассмотрим этот код:

public function populateUserList (users:Array):void { try {

// Приступаем к выполнению этапа 1… получаем экземпляр класса List. // Если метод getllserListC ) сгенерирует исключение, будет выполнен // внешний блок catch.

var ulist:List = getGUIManager( ).getUserList( ); // Приступаем к выполнению этапа 2… заполняем экземпляр класса List, for (var i-.Number = 0; i < users.length: i++) { try {

var thisUser:User = User(users[i]);

// Если метод getID( ) сгенерирует исключение, будет выполнен // вложенный блок catch. В противном случае пользователь будет // добавлен в экземпляр класса List вызовом метода addltem( ). ulist. addItem(thisUser. getName( ). thisUser. getID( )); } catch (e:UserIdNotSet) { trace(e. message);

continue; // Пропускаем этого пользователя.

}

}

} catch (e:UserListNotFound) { trace(e. message);

}

}

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

Руководство по actionscript. часть 2, стр. 096

Если исключение генерируется в блоке try, вложенном в другой блок try, и внутренний блок try содержит блок catch, способный обработать сгенерированное исключение, выполняется внутренний блок catch и программа продолжает свое выполнение сразу после внутренней инструкции try/catch/finally.

try { try {

// Здесь генерируется исключение, throw new ErrorCTest error»); } catch (e;Error) { // Здесь обрабатывается исключение. trace(e. message); // Выводит; Test error

}

// Здесь продолжается выполнение программы. } catch (e:Error) { // Обработка исключений, генерируемых внешним блоком try.

}

Если, с другой стороны, исключение возникло в блоке try, вложенном в другой блок try, однако внутренний блок try не содержит блока catch, способного обработать данное исключение, сгенерированное исключение будет передаваться вверх к внешней инструкции try/catch/finally (и при необходимости дальше по стеку вызовов) до тех пор, пока не будет найден подходящий блок catch или пока не будет достигнута верхняя точка стека вызовов. Если исключение будет обработано в некоторой точке стека вызовов, то выполнение программы продолжится сразу после инструкции try/catch/finally, обработавшей это исключение. Обратите внимание, что в следующем примере кода (и последующих примерах) гипотетический тип данных ошибки SomeSpecificEr ror является заполнителем, используемым для того, чтобы сгенерированное исключение не было поймано. Чтобы протестировать пример кода в вашем собственном коде, вы должны создать подкласс SomeSpecificError класса Error.

try { try {

// Здесь генерируется исключение, throw new Error(«Test error»); } catch (e;SomeSpecificError) { // Здесь исключение не обрабатывается. trace(e. message); // Инструкция никогда не будет выполнена, // поскольку типы не совпадают.

}

} catch (е:Error) { // Исключение обрабатывается здесь. trace(e. message); // Выводит; Test error

}

// Выполнение программы продолжается здесь, сразу после обработки исключения // внешним блоком catch.

Руководство по actionscript. часть 2, стр. 097

Если исключение генерируется в блоке try, вложенном в блок catch, и внутренний блок try не имеет блока catch, способного обработать данное исключение, поиск подходящего блока catch начинается за пределами внешней инструкции try/ catch/finally:

try {

// Здесь генерируется внешнее исключение, throw new Error(«Test error 1″); } catch (e;Error) { // Здесь обрабатывается внешнее исключение. trace(e. message); // Выводит: Test error 1 try {

// Здесь генерируется внутреннее исключение, throw new ErrorC’Test error 2″); } catch (e:SomeSpecificError) { // Внутреннее исключение здесь не обрабатывается. trace(e. message); // Инструкция никогда не будет выполнена, // поскольку типы не совпадают.

}

}

// Процесс поиска подходящего блока catch для внутреннего исключения // начинается здесь.

Наконец, если исключение генерируется в блоке try, вложенном в блок finally, но при этом предыдущее исключение уже находится в процессе подъема по стеку вызовов, новое исключение будет обработано до того, как предыдущее исключение продолжит свой подъем по иерархии объектов.

// Этот метод генерирует исключение в блоке finally, public function throwTwoExceptions ( ):void { try {

// Здесь генерируется внешнее исключение. Поскольку этот блок try // не имеет соответствующего блока catch, внешнее исключение начинает // свой подъем по иерархии объектов, throw new ErrorC’Test error 1″); } finally { try {

// Здесь возникает внутреннее исключение. Внутреннее исключение

// будет обработано до того, как внешнее исключение фактически начнет

// подъем по иерархии объектов, throw new ErrorC’Test error 2″); } catch (e:Error) { // Внутреннее исключение обрабатывается здесь, tracer Internal catch: » + e. message);

}

}

}

// Где-то в другом месте, внутри метода, // вызывающего предыдущий метод, try {

throwTwoExceptions( ); } catch (e:Error) { // Здесь обрабатывается внешнее исключение, // поднявшееся из метода throwTwoExceptions( ). trace(«External catch: » + e. message);

}

// Вывод (обратите внимание, что внутреннее исключение обработано первым): // Internal catch: Test error 2 // External catch: Test error 1

Если бы в предыдущем коде исключение, сгенерированное в блоке finally, не было обработано, среда Flash сообщила бы об ошибке на этапе отладки и прекратила бы выполнение оставшегося кода в стеке вызовов. В результате никакой код в стеке вызовов не обработал бы сгенерированное первым внешнее исключение. Следующий код демонстрирует описанный сценарий. Этот код генерирует необрабатываемое исключение в блоке finally. В итоге исключение, сгенерированное внешним блоком try, будет потеряно.

Руководство по actionscript. часть 2, стр. 098

Try {

// Здесь генерируется внешнее исключение, throw new ErrorC’Test error 1″); } finally { try {

// Здесь генерируется внутреннее исключение, throw new ErrorC’Test error 2″); } catch (e:SomeSpecificError) { // Внутреннее исключение здесь не обрабатывается. trace(«internal catch: » + e. message); // Инструкция никогда не будет

// выполнена, поскольку типы

// не совпадают.

}

}

// Процесс поиска подходящего блока catch для внутреннего исключения // начинается здесь. Если внутреннее исключение не будет обработано, о нем // сообщит среда выполнения Flash на этапе отладки, при этом передача // внешнего исключения вверх по иерархии объектов будет прекращена.

Предыдущий код демонстрирует влияние необработанного исключения на ход выполнения программы в одном сценарии, однако хочется еще раз повторить, что

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

Изменение хода выполнения программы в инструкции try/catch/finally

На протяжении этой главы мы неоднократно убеждались в том, что инструкция throw изменяет ход выполнения программы. Когда среда Flash встречает инструкцию throw, она немедленно прекращает выполнение текущей задачи и передает управление программой подходящим блокам catch и finally. Тем не менее блоки catch и finally также могут влиять на ход выполнения программы с помощью оператора return (в случае метода или функции), break или continue (в случае цикла). Более того, операторы return, break и continue могут также использоваться в блоке try.

Чтобы познакомиться с правилами изменения хода выполнения программы в инструкции try/catch/finally, рассмотрим, как оператор return влияет на ход выполнения программы в блоках try, catch и finally. Следующий пример кода содержит функцию changeFlow ( ), которая демонстрирует ход выполнения программы в различных гипотетических ситуациях.

Руководство по actionscript. часть 2, стр. 099

В листинге 13.1 показан оператор return в блоке try, помещенный перед инструкцией, генерирующей ошибку. В данном случае метод выполняется нормально и никакая ошибка не будет сгенерирована или обработана. Однако перед возвратом из метода будет выполнен блок finally. Стоит отметить, что вы вряд ли увидите код, в точности повторяющий код из листинга 13.1, в реальной программе. В большинстве случаев оператор return используется в условных операторах и выполняется в ответ на некоторое определенное условие в программе.

Листинг 13.1. Использование оператора return в блоке try перед оператором throw

public function changeFlow ( ):void { try { return;

throw new ErrorC’Test error.»);

} catch (e:Error) {

trace(«Caught: » +- e. message); } finally {

trace(«Finally executed.»);

}

traceC’Last line of method.»);

}

// Вывод после вызова метода changeFlow( ): // Finally executed.

В листинге 13.2 показан оператор return в блоке try, помещенный после инструкции, генерирующей ошибку. В данном случае return не выполнится, поскольку ошибка будет сгенерирована до того, как программа дойдет до этого оператора. Как только

ошибка будет обработана и выполнение инструкции try/ cat ch/finally завершится, выполнение программы продолжится после данной инструкции try/catch/finally и выход из метода произойдет в конце его тела. Снова повторим, что листинг 13.2 всего лишь демонстрирует принцип, но не является типичным в реальном сценарии, поскольку ошибка обычно генерируется на основании некоторого условия.

Руководство по actionscript. часть 2, стр. 100

Листинг 13.2. Использование оператора return в блоке try после оператора throw

public function changeFlow ( ):void { try {

throw new ErrorC’Test error.»); return;

} catch (e:Error) {

trace(«Caught: » + e. message); } finally {

trace(«Finally executed.»);

}

traceC’Last line of method.»);

}

// Вывод после вызова метода changeFlow( ): // Caught: Test error. // Finally executed. // Last line of method.

Листинг 13.3 демонстрирует использование оператора return в блоке catch. В данном случае return будет выполнен после завершения процесса обработки ошибки. При этом код, размещенный после инструкции try/catch/finally, выполнен не будет. Однако, как обычно, перед возвратом из метода будет выполнен блок fin а 11 у. В отличие от листингов 13.1 и 13.2 данный код является типичным в реальном сценарии, когда выполнение метода прекращается при возникновении ошибки.

Листинг 13.3. Использование оператора return в блоке catch

public function changeFlow ( ):void { try {

throw new ErrorC’Test error.»); } catch (e:Error) {

trace(«Caught: » + e. message);

return; } finally {

traceC’Finally executed.»);

}

traceC’Last line of function.»);

}

// Вывод после вызова метода changeFlow( ): // Caught: Test error. // Finally executed. ,

Из-за известной ошибки в приложении Flash Player 9 при выполнении кода из листингов 13.2 и 13.3 происходит обращение к несуществующей области стека. Корпорация Adobe планирует исправить эту проблему в следующей версии приложения Flash Player.

Руководство по actionscript. часть 2, стр. 101

В листинге 13.4 показан оператор return в блоке finally. В данном случае он выполняется тогда, когда выполняется блок finally (как уже известно, выполнение блока finally происходит после завершения соответствующего блока try одним из следующих способов: без ошибок; с обработанной ошибкой; с необработанной ошибкой; в результате вызова операторов return, break или continue). Обратите внимание, что оператор return в листинге 13.4 предотвращает выполнение кода, размещенного после инструкции try/catch/ finally. Вы можете использовать подобную методику для завершения метода после выполнения блока кода независимо от того, было сгенерировано исключение или нет. При этом вся инструкция try/catch/finally обычно помещается внутрь условного оператора (иначе оставшаяся часть метода никогда не будет выполнена!).

Листинг 13.4. Использование оператора return в блоке finally

public function changeFlow ( ):void { try {

throw new ErrorC’Test error.»); } catch (e:Error) {

trace(«Caught: » + e. message); } finally {

trace(«Final! у executed.»);

return;

}

traceC’Last line of method.»); // He выполняется.

}

// Вывод после вызова метода changeFlow( ): Caught: Test error. Finally executed.

^ I Если оператор return в блоке finally вызывается после того, как был вызван оператор м$ 4 * return в соответствующем блоке try, вызов return в блоке finally отменяет предыдущий цу вызов return.

Обработка предопределенного исключения

В самом начале этой главы мы узнали, что с помощью инструкции try/catch/ finally можно обрабатывать как предопределенные, так и пользовательские ошибки. До настоящего момента все обрабатываемые нами ошибки были пользовательскими. Сейчас мы рассмотрим инструкцию try/catch/finally, обрабатывающую предопределенную ошибку.

Руководство по actionscript. часть 2, стр. 102

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

используя класс Socket. В некоторых случаях установить соединение не получится из-за ограничений безопасности. Чтобы показать, что причиной неудачной попытки соединения являются ограничения безопасности, среда выполнения Flash генерирует исключение SecurityError. Таким образом, при попытке создать соединение мы помещаем соответствующий код в блок try. Если установить соединение не получится по причинам безопасности, мы отобразим для пользователя сообщение об ошибке, обозначив проблему, из-за которой возникла данная ошибка.

var socket:Socket = new Socket( ); try {

// Пытаемся подключиться к указанному порту socket. connect(«example. com». userPort); } catch (e:SecurityError) { // Код. расположенный здесь, отображает сообщение для пользователя

}

I Список причин, которые могут привести к ошибкам соединения сокета, можно найти м?’ л * в описании метода connect() класса Socket в справочнике по языку ActionScript корпо-цу рации Adobe.

События об ошибках в случае проблемных ситуаций. В предыдущем разделе мы рассмотрели, как обработать исключение, вызванное недопустимой попыткой установить соединение сокета. Однако не все сбойные ситуации в языке ActionScript приводят к генерации исключений. Информация о проблемах, возникающих асинхронно (то есть спустя некоторое время), передается через события об ошибках, а не через исключения. Например, если мы попытаемся загрузить файл, среда выполнения Flash в асинхронном режиме сначала должна проверить, существует ли запрашиваемый файл. Если этот файл не существует, среда Flash выполнит диспетчеризацию события IOErrorEvent. IO_ERROR. Чтобы обработать возникшую проблему, код, инициировавший операцию загрузки, должен зарегистрировать обработчик для события IOErrorEvent. IO ERROR. Если этого не произойдет, возникнет ошибка на этапе выполнения. Пример обработчика события об ошибке можно найти среди примеров в подразд. «Два дополнительных примера регистрации приемников событий» разд. «Основы обработки событий в ActionScript» гл. 12.

Впереди еще одна скучная работа

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

ПИВА 14

Сборка мусора

Всякий раз, когда программа создает объект, среда выполнения Flash сохраняет его в системной памяти (например, ОЗУ). По мере того как программа создает сотни, тысячи или даже миллионы объектов, объем памяти, занимаемой этой программой, постепенно увеличивается. Чтобы предотвратить полное исчерпывание системной памяти, Flash автоматически удаляет из нее объекты, когда программа перестает в них нуждаться. Процесс автоматического удаления объектов из памяти называется сборкой мусора.

Доступность объектов для сборки мусора

В программе на языке ActionScript любой объект становится доступным для процесса сборки мусора сразу после того, как он окажется недостижимым. Объект считается недостижимым, когда к нему невозможно обратиться прямо или косвенно хотя бы через один корневой элемент сборки мусора. К наиболее значимым корневым элементам сборки мусора в языке ActionScript можно отнести следующие:

? переменные, определенные на уровне пакета;

? локальные переменные в выполняющемся методе или функции;

? статические переменные;

? переменные экземпляра основного класса программы;

? переменные экземпляра объекта, находящегося в списке отображения среды выполнения Flash;

? переменные, находящиеся в цепочке областей видимости выполняющейся функции или метода.



Полезные ссылки
Случайные записи
  • 23.02.2011">Руководство по actionscript. часть 7, стр. 027
  • 10.05.2010">Самоучитель по креативному веб-дизайну. Книга 1, стр.106
  • 22.03.2011">Руководство по actionscript. часть 2, стр. 026
  • 10.05.2010">Самоучитель по креативному веб-дизайну. Книга 1, стр.119
  • 12.03.2011">Руководство по actionscript. часть 3, стр. 118
  • 15.01.2010">Flash сайты
  • 16.03.2011">Руководство по actionscript. часть 3, стр. 008
  • 09.05.2010">Самоучитель по креативному веб-дизайну. Книга 1, стр.147
  • 26.02.2011">Руководство по actionscript. часть 6, стр. 064
  • 15.07.2012">Англоязычные статьи Wikipedia можно будет комментировать
  • 23.01.2011">Руководство по actionscript. часть 1, стр. 011
  • 23.01.2011">Руководство по actionscript. часть 1, стр. 017
  • 19.07.2010">Тестируем сайт правильно
  • 27.01.2012">Покажут ли нам в феврале iPad 3?
  • 11.05.2010">Самоучитель по креативному веб-дизайну. Книга 1, стр.26
Опрос

Какие цвета вы предпочитаете?

View Results

Loading ... Loading ...