Шрифт:
Практика надежного программирования обязывает предусматривать проблемы, которые могут возникнуть в программе. Это можно делать, проверяя состояния, которые могут вызвать аварийное завершение программы, и включая обработку этих ситуаций, например, выводя сообщение и корректно завершая программу. Например, выше в этой главе было показано, как проверить, отвечает ли объект на определенное сообщение. Эта проверка позволяет избежать отправки нераспознаваемого сообщения. Обычно при попытке отправки нераспознаваемою сообщения профамма сразу прекращает свою работу, выдавая гак называемую исключительную ситуацию, или исключение (exception).
Рассмотрим программу 9.4. В определении класса Fraction у нас не было метода с именем noSuchMethod («нет такого метода»). При компиляции этой программы вы получите из-за этого предупреждающие сообщения. #import Traction.h" int main (int arge, char *argv []) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; Fraction *f = [[Fraction alloc] init]; [f noSuchMethod]; NSLog (@"Execution continues!'’); [f release]; [pool drain]; return 0; }
Несмотря на эти предупреждающие сообщения, вы можете попытаться продолжить работу и выполнить программу. В этом случае программа будет аварийно завершена с выводом следующих сообщений об ошибках. Вывод программы 9.4 -[Fraction noSuchMethod]: unrecognized selector sent to instance 0x103280 (... нераспознанный селектор передан экземпляру) *** Terminating арр due to uncaught exception ’NSInvalidArgumentException', (Прекращение работы приложения из-за необработанного исключения) reason: ’*** -[Fraction noSuchMethod]: unrecognized selector sent to instance 0x103280’ (причина: ... нераспознанный селектор передан экземпляру) Stack: ( 2482717003, 2498756859, 2482746186, 2482739532, 2482739730 ) Trace/BPT trap
Чтобы избежать аварийного завершения программы, можно поместить оди н или несколько операторов в блоке операторов, имеющем следующий формат. @try { оператор оператор } @catch (NSException *exception) { оператор оператор )
Выполнение программы в блоке @try происходит как обычно. Однако если один из операторов в этом блоке выдает исключение, работа программы нс прекращается, а управление передастся в блок @catch, где продолжается ее выполнение. Внутри этого блока можно обрабатывать исключение. Последовательностью действий в этом случае может быть вывод сообщения об ошибке, очистка и завершение работы программы.
В профамме 9.5 показана обработка исключения. Затем приводится вывод программы. #import "Fraction.h" int main {int arge, char *argv []) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; Fraction *f = [[Fraction alloc] init]; @try { [f noSuchMethod]; } @catch (NSException *exception) { NSLog(@"Caught %@%@", [exception name], [exception reason]); } NSLog (@"Execution continues!"); [f release]; [pool drain]; return 0; }
Вывод программы 9.5 *** -[Fraction noSuchMethod]: unrecognized selector sent to instance 0x103280 Caught NSInvalidArgumentException: *** -[Fraction noSuchMethod]: unrecognized selector sent to instance 0x103280 Execution continues! (Выполнение продолжается!)
Если возникает исключительная ситуация, выполняется блок @catch. Объект NSExceptton, который содержит информацию об исключении, передастся в этот блок как аргумент. Мметод name считывает имя исключения, а метод reason указывает причину (которую система runtime раньше выводила автоматически). После выполнения последнего оператора в блоке @catch (здесь только один оператор) программа продолжает выполнение, начиная с оператора, который непосредственно следует за этим блоком. В данном случае мы выполняем вызов NSLog, чтобы подтвердить, что выполнение не было прекращено.
Это очень простой пример, показывающий, как перехватывать исключения в профамме. Можно также использовать блок @finally, чтобы включить код, выполняемый независимо от возникновения исключительной ситуации в каком- либо операторе блока @try.
Директива @throw позволяет создавать ваше собственное исключение. Ее можно использовать для создания конкретного исключения или внугри блока @catch для создания той же исключительной ситуации, которая вызвала переход в этот блок: @throw;
Это может потребоваться после вашей собственной обработки исключения (например, после выполнения операций очистки). После этого вы можете передать системе остальную часть работы. И, наконец, у вас может быть несколько блоков @catch, которые следуют в определенном порядке для перехвата и обработки исключений различного типа. Упражнения
Что произойдет, если вставить выражение с сообщением [compResult reduce]; в профамму 9.1 после того, как выполнено сложение (но до выполнения release для compResult)? Попробуйте и посмотрите, что получится.
Можно ли переменной dataValue типа id (определенной в профамме 9.2) присвоить объект класса Rectangle в соответствии с его определением в главе 8? Иначе говоря, является ли допустимым оператор dataValue = [[Rectangle alloc] init]; Почему?
Добавьте метод print к классу XYPoint, определенному в главе 8. Он должен выводить точку в формате (х,у). Затем внесите изменения в программу 9.2, чтобы включить объект типа XYPoint. Эта модифицированная программа должна создавать объект типа XYPoint, задавать его значение, присваивать его переменной dataValue типа id и затем выводить его значение.
Вспомните, что говорилось в этой главе о типах ар1ументов и возвращаемых значений, и модифицируйте методы add: в классах Fraction и Complex, чтобы принимать и возвращать объекты типа id. Затем напишите программу, которая включает следующую последовательность кода. result = [dataValuel add: dataValue2]; [result print];
Здесь result, dataValuel и dataValue2 — это объекты типа id. He забудьте задать образом значения dataValuel и dataValue2 в программе и освободить (release) все объекты, прежде чем завершить программу.