При переносе кода Java в ObjC, как лучше всего представлять проверенные исключения?
Я работаю над переносом кодовой базы Java на Cocoa/Objective-C для использования на настольном Mac OS X. В коде Java есть множество методов с проверенными исключениями, такими как:
double asNumber() throws FooException {
...
}
Как лучше всего представить их в Objective-C? Исключения или выходные параметры ошибки?
- (CGFloat)asNumber {
... // possibly [FooException raise:format:];
}
или же
- (CGFloat)asNumberError:(NSError **)outError {
...
}
У меня есть ощущение, что out-ошибки, как правило, являются лучшим решением для Objective-C, но, как вы можете видеть... многие методы, подобные приведенному выше, будут выглядеть довольно неловко. И опять же, их много.
Конечно, имейте в виду, что, поскольку это проверенные исключения в Java, мне нужно будет добавить либо @try
блоки или if (*outError) {...}
проверяет, где эти методы вызываются (много мест).
Я помню, что слышал, что при входе @try
Когда-то блоки были в Objective-C дорогостоящими, в 64-битной или SL-версии, или в каком-либо другом новом env-коде, они дешевы (точно не помню). Меня совсем не беспокоит обратная совместимость, поэтому я определенно хочу разрабатывать только для новой популярности.
6 ответов
Вы должны определенно избегать исключений для таких вещей, как разбор чисел из строк. В Objective-C исключения представляют ошибку программиста, а не ошибку ввода пользователя или даже недоступные файлы. (Отчасти причина в том, что обработка исключений всегда более затратна и сложна, чем более "обычная" обработка ошибок. Независимо от того, что ввод @try
Blocks - это "нулевая стоимость" в 64-битной среде, она все еще медленная, когда действительно возникает исключение.) Конечно, вы можете использовать исключения по своему усмотрению, но это не так, как в Какао, и вы окажетесь на расходится с другим кодом Objective-C. Люди, которые используют ваш код, будут невероятно раздражены тем, что вы генерируете исключения в случаях, которые могут привести к ошибке.
Из собственных документов Apple:
"Во многих средах использование исключений является довольно распространенным явлением. Например, вы можете выдать исключение, чтобы указать, что подпрограмма не может быть выполнена нормально - например, когда файл отсутствует или данные не могут быть проанализированы правильно. Исключения являются ресурсоемкими. в Objective C. Вы не должны использовать исключения для общего управления потоком или просто для обозначения ошибок. Вместо этого вы должны использовать возвращаемое значение метода или функции, чтобы указать, что произошла ошибка, и предоставить информацию о проблеме в объект ошибки."
Посмотрите, как встроенные классы Какао обрабатывают подобные ошибки. Например, NSString имеет такие методы, как -floatValue
это возвращает 0, если это не удается. Лучшим решением для вашей конкретной ситуации может быть то, как это делает NSScanner, например, в -scanFloat:
- принять указатель на поле, где должен быть сохранен результат, и вернуть YES
или же NO
основанный на том, был ли анализ успешным.
Помимо соглашения и рекомендаций Obejctive-C, NSError гораздо более надежен и гибок, чем NSException, и позволяет вызывающему абоненту эффективно игнорировать проблему, если он этого хочет. Я предлагаю прочитать руководство по программированию обработки ошибок для какао. Примечание: если вы принимаете NSError**
param, я настоятельно рекомендую вам также дизайн, чтобы позволить клиенту пройти NULL
если они не хотят получать какую-либо информацию об ошибке. Каждый известный мне класс Какао делает это из-за ошибок, включая NSString.
Хотя портированный код может в конечном итоге выглядеть совсем не так, как Java-код, следует понимать, что он будет использоваться кодом Objective C, а не теми же клиентами, что и Java-эквивалент. Определенно соответствует идиомы языка. Порт не будет зеркальным отображением кода Java, но в результате он будет намного более корректным (для Objective-C).
В Какао исключения действительно должны использоваться только для "ошибок программирования"; философия заключается в том, чтобы приложение могло их поймать, дать пользователю возможность сохранить то, что они делают, и выйти. С одной стороны, не все фреймворки или пути кода могут быть на 100% безопасными для исключений, поэтому это может быть единственным безопасным способом действий. Для ошибок, которые можно предвидеть и исправить, следует использовать NSError, как правило, через выходной параметр.
Вы правы, что "наши ошибки - лучшее решение для ObjC". Очень редко вы найдете API в Какао, который выдает исключение (если вы не выполнили предварительные условия для API, но в этом случае поведение по умолчанию не определено).
Если вы ожидаете, что этот код будет жить за пределами вас и будет принят другими разработчиками Cocoa, я бы порекомендовал использовать ошибки. Я работаю над кодом, который был создан людьми, незнакомыми с Какао и свободно использовавшими исключения, и обходиться без них - настоящая боль.
Я большой поклонник подхода Out Error, который использует Objective-C. Вы должны обрабатывать исключения, но вы можете игнорировать ошибки, если хотите. Все это соответствует взгляду Objective-C на то, что "программист знает, что делает". Это также делает Objective-C очень чистым языком, потому что ваш код не загроможден блоками try-catch.
Тем не менее, вы можете подумать: есть ли сценарии, где исключения игнорируются? Являются ли исключения, которые вы выбрасываете, действительно критичными? Вы пишете простые блоки catch, которые очищают переменные и продолжают работу? Я бы склонялся к ошибкам, потому что мне нравятся исключения из синтаксиса и резервов Objective-C только для самых критических ошибок.
Похоже, эти проверенные исключения более четко отображают ошибки. Исключения могут все еще использоваться, но должны быть зарезервированы для исключительных обстоятельств.
Исключения, вероятно, являются наилучшим подходом, так как 64-разрядный ABI Obj-C (среда выполнения) использует исключения с нулевой стоимостью, поэтому вы получаете более чистый код без реальной стоимости. Конечно, в 32-битных версиях старые исключения setjmp/longjmp все еще используются, и они не взаимодействуют с C++, поэтому, если это цель, у вас есть проблема.