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

Для этого следует выбрать команду Product ^Analyze (<Command+Shift+B>), которая выполняет принудительную компиляцию кода, а статический анализатор глубоко исследует его, выдавая сообщения в окне навигатора и в самом коде. Как и при обычной компиляции, среда Xcode 5 может анализировать отдельный файл (выберите команду Product^ Perform Action ^Analyze [Filename]).

Статический анализатор анализирует код, а не выполняет его отладку в реальном времени, но это очень интеллектуальный и совершенный инструмент. Благодаря этому он может сообщить о потенциальных проблемах, которые могли бы ускользнуть от вашего внимания. Вам может показаться, что для этого достаточно компилятора. Действительно, некоторые из возможностей статического анализатора перекочевали в него из компилятора, который в среде Xcode 5 стал еще более интеллектуальным и полезным. Более того, одна из основных причин использования статического анализатора — желание облегчить ручное управление памятью в экземплярах языка Objective-C, — по большей мере обусловлено использованием механизма ARC. Однако не все вопросы управления памятью учитываются механизмом; например, он не выполняет управление памятью ARC с помощью ссылок CFTypeRef (глава 12), в то время как анализатор предупреждает о возможных ошибках. Статический анализатор затрачивает время на глубокое исследование возможных значений и путей выполнения кода и может выявлять возможные источники проблем, связанных с нарушением логики программы, которые компилятор игнорирует.

Например, недавно я выполнил статический анализ следующего кода:

-(void) newGameWithlmage:(id)imageSource song:(NSString*)song {

CGImageSourceRef src;

if ([imageSource isKindOfClass:[NSURL class]]) src = CGImageSourceCreateWithURL(

( bridge CFURLRef)imageSource, nil);

if ([imageSource isKindOfClass:[NSData class]]) src = CGImageSourceCreateWithData (

 

// ...

if (nil != src)

CFRelease(src); }

Во-первых, анализатор сообщил, что переменная src имеет мусорное значение. Это показалось мне неправдоподобным; ведь переменная src должна была быть инициализирована одним из двух условий метода isKindOfClassПотом я понял, что анализатор не знал об этом, как и я. Я хотел, чтобы этот метод вызывался со значением imageSource, которое должно быть объектом класса NSURL или NSData; а что, если какой-нибудь идиот вызвал бы этот метод со значением другого типа? Тогда очевидно, что переменная src останется неинициализированной. По этой причине я инициализировал ее значением nil:

 

CGImageSourceRef src = nil;

 

Я запустил статический анализатор снова, но он все еще выдавал сообщения о том, что переменная src приводит к потенциальной утечке памяти по завершении метода. Как это возможно? Правила управления памятью с помощью ссылок CFTypeRef требуют, чтобы программист создал объект класса CGImageSourceRef, вызывающий метод CGImageSourceCreateWithURL или CGImageSourceCreateWithData, а затем вызвал метод CFRelease для удаления этого объекта. Я так и сделал.

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

нения программы. Я сделал это и увидел, что анализатор считает, что оба условия метода isKindOfClass: могут выполняться одновременно (рис. 9.8). Если это так, то первое значение переменной src может исчезнуть: она будет заменена вторым значением без каких-либо операций по управлению памятью!

 

 Статический анализатор рисует диаграмму

Puc. 9.8. Статический анализатор рисует диаграмму

 

Из принципов наследования классов следует, что оба условия метода isKindOfClass: не могут выполняться одновременно; объект imageSource должен относиться к классу NSURL или NSData (или ни к одному из них). Однако статический анализатор интересует логика и поток выполнения программы. Если я считаю, что может выполняться только одно из этих условий, я должен выразить этот факт логически и структурно. Я так и сделал, заменив второй оператор if на else if:

if ([imageSource isKindOfClass:[NSURL class]]) src = CGImageSourceCreateWithURL(

( bridge CFURLRef)imageSource, nil);

else if ([imageSource isKindOfClass:[NSData class]]) src ® CGImageSourceCreateWithData(

( bridge CFDataRef)imageSource, nil);

 

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

Для того чтобы статический анализатор автоматически запускался как часть обычной компиляции при сборке, можно использовать настройку сборки Analyze During 'Build'. Если установить ее значение равным Yes, то целесообразно установить параметр Mode of Analysis for 'Build' равным Shallow; полный анализ (Deep) затрачивает слишком много времени.


 

 

 

Добавить комментарий