Программирование на Objective-C 2.0
Шрифт:
Примечание. Вы можете отлавливать ошибку, переопределив метод doesNotRecognize:. Этот метод вызывается, когда классу передано нераспознаваемое сообщение и в качестве аргумента передан нераспознаваемый селектор. Можно применять и другие стратегии. Например, с помощью метода forward:: можно перенаправлять сообщение для обработки. Всегда можно передавать метод и перехватывать возникшую исключительную ситуацию. Мы рассмотрим этот способ чуть позже.
В программе 9.3 задаются некоторые вопросы о классах Square и Rectangle, определенных в главе 8. Постарайтесь предсказать результаты этой программы. #import "Square.h" int main (int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; Square *mySquare = [[Square alloc] init]; // Проверка с помощью isMemberOf: if ( [mySquare isMemberOfClass: [Square class]] == YES ) NSLog (@"mySquare is a member of Square class"); if ( [mySquare isMemberOfClass: [Rectangle class]] == YES ) NSLog (@"mySquare is a member of Rectangle class"); if ( [mySquare isMemberOfClass: [NSObject class]] == YES ) NSLog (@"mySquare is a member of NSObject class"); II
Эта программа должна быть создана с файлами секции implementation для классов Square, Rectangle и XYPoint, которые были рассмотрены в главе 8.
Вывод программы 9.3 mySquare is a member of Square class (mySquare является членом класса Square) mySquare is a kind of Square (mySquare происходит из Square) mySquare is a kind of Rectangle mySquare is a kind of NSObject mySquare responds to setSide: method (mySquare отвечает на метод setSide:) mySquare responds to setWidthiandHeight: method Square class responds to alloc method (Square отвечает на метод alios) Instances of Square respond to setSide: method (Экземпляры mySquare отвечают на метод setSide:) Square is a subclass of a rectangle (Square - это подкласс класса Rectangle)
Вывод программы 9.3 вполне понятен. isMemberOfClass: прозеряет непосредственное членство в классе, a isKindOfClass: проверяет членство в иерархии наследования. mySquare является членом класса Square и «происходит» из Square, Rectangle и NSObject, поскольку входи т в иерархию этою класса (очевидно, что все объекты должны возвращать значение YES для проверки isKindOf: по классу NSObject, если только вы не определили новый корневой объект).
В проверке if ( [Square respondsTo: @selector (alloc)] == YES )
определяется, отвечает ли класс Square на метод класса alloc. Это так, поскольку он унаследован из корневого объекта NSObject. Вы всегда можете использовать непосредственно имя класса в качестве получателя в выражении для сообщения и не обязаны включать [Square class]
в предыдущее выражение (хотя могли бы это сделать).
Но это единственное место, где можно без этого обойтись. В других местах для получения объекта-класса нужно обязательно применять метод class. 9.6. Обработка исключительных ситуаций с помощью @try
Практика надежного программирования обязывает предусматривать проблемы, которые могут возникнуть в программе. Это можно делать, проверяя состояния, которые могут вызвать аварийное завершение программы, и включая обработку этих ситуаций, например, выводя сообщение и корректно завершая программу. Например, выше в этой главе было показано, как проверить, отвечает ли объект на определенное сообщение. Эта проверка позволяет избежать отправки нераспознаваемого сообщения. Обычно при попытке отправки нераспознаваемою сообщения профамма сразу прекращает свою работу, выдавая гак называемую исключительную ситуацию, или исключение (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 и затем выводить его значение.