Простое объявление типа ссылки на экземпляр не влечет за собой существование соответствующего экземпляра.

Например:

NSString* s; // Только объявление; экземпляра, на который
// указывает s, не существует

После этого объявления s имеет тип указателя на NSString, но на самом деле эта переменная не указывает на NSString. Вы создали указатель, но не предоставили объект NSString, на который он должен указывать. Он пока что ждет, пока вы укажете, на что он должен указывать (обычно путем присваивания, как мы это делали с помощью строки @"Hello, world! " вйше). Такое присваивание инициализирует переменную, придавая ей первоначальное имеющее смысл значение надлежащего типа.

Можно объявить переменную как ссылку на экземпляр в одной строке кода, а инициализировать ее позже, как в приведенном примере:

NSString* s;
// ... Прошло время ...
s = @"Hello, world!";

Однако обычно так не делается. Гораздо более распространена практика объявления и инициализации переменной в одной строке кода (если это возможно):

NSString* s = @"Hello, world!";

Объявление без инициализации до появления механизма ARC (автоматического подсчета ссылок, см. главу 12) приводило к достаточно опасным ситуациям.

NSString* s;

Без механизма ARC переменная s после объявления может иметь любое значение. Беда в том, что эта переменная претендует на то, чтобы являться указателем на NSString. Обманувшись, вы можете рассматривать мусор в памяти, на который указывает переменная s, как строку NSString. Это может привести к серьезным неприятностям, если вы попытаетесь использовать мусор в качестве экземпляра. Отправка сообщения мусору или использование его иным образом может привести к сбою программы. Еще хуже — это может не привести к сбою программы, а всего лишь заставить ее работать неверно, — и при этом будет очень, очень сложно выяснить, в чем же причина такого поведения программы.

Назначение ссылке значения nil приводит к тому, что, хотя она и не указывает ни на какой реальный экземпляр, она не указывает и на мусор. До появления механизма ARC распространенной защитой от “мусорных указателей” была инициализация каждого указателя в момент его объявления значением nil, если инициализация реальным объектом по каким-то причинам оказывается в этот момент невозможной:

NSString* s = nil;

Небольшим, но очаровательным дополнением к возможностям механизма ARC является то, что такое присваивание выполняется автоматически, неявно и незаметно, в случае объявления переменной без ее инициализации:

NSString* s; // ARC присваивает s значение nil

Что такое nil? Это разновидность нуля — нуль, который подходит для присваивания ссылке на экземпляр. Значение nil просто означает: “эта ссылка на экземпляр не указывает ни на один реальный экземпляр”. Действительно, вы можете проверить, указывает ли ссылка на реальный экземпляр, сравнив ее с nil. Это очень распространенная практика:

if (nil — s) II ...

Как я упоминал в главе 1, явное сравнение с nil не является строго необходимым; поскольку nil является разновидностью нуля, а также, так как нуль в условии означает ложь, тот же тест можно выполнить проще:

if (!в) II ...

Я буду использовать именно эту, вторую разновидность проверки, но некоторые программисты будут считать, что у меня плохой стиль программирования. Первая разновидность имеет то преимущество, что ее реальный смысл сразу становится очевиден всем и не приходится полагаться на нехоторую неявную функциональность С. В первой разновидности nil также преднамеренно занимает место слева от оператора ", так что если программист случайно пропустит один знак равенства, выполняя вместо сравнения присваивание, компилятор обнаружит эту ошибку (потому что присваивать что-то nil нельзя).

Многие методы Cocoa используют возвращаемое значение nil вместо ожидаемого экземпляра для указания, что что-то выполнено не так, как надо. Чтобы убедиться, что вызов метода прошел успешно, вы должны проверить возвращаемое значение на равенство nil. Например, документация метода stringWithContentsOfFile: encoding: error: класса NSString гласит, что он “возвращает строку, полученную путем считывания данных из файла с именем path, используя кодировку епс. Если файл не может быть открыт или имеется ошибка кодирования, возвращается значение nil”. Так что, как я писал в главе 1, этот метод должен вызываться следующим образом:

NSString* path =  // Какое-то имя
NSStringEncoding enc =  // Какое-то значение
NSError* err = nil;
NSString* result =
[NSString stringWithContentsOfFile: path encoding: enc error: serr];

Почему stringWithContentsOf File : encoding: error: имеет такой вид? По сути этот метод может возвращать два результата. Он возвращает строку, которую мы сохраняем, присваивая ее указателю на NSString, и которую мы называем результатом. Но если произошла ошибка, то метод задает значение другого объекта, а именно объекта NSError. Идея заключается в том, что вы можете впоследствии изучить этот объект NSError и выяснить, что именно пошло не так. (Возможно, не было файла с указанным именем, или он имел неверную кодировку.) Передавая указатель на указатель на NSError, вы позволяете методу выполнить описанное действие. Перед вызовом stringWithContentsOfFile: encoding:erro г: переменная err была инициализирована значением nil; во время вызова stringWithC ontentsOfFile:encoding:error:, если произошла ошибка, указатель перенаправляется, тем самым присваивая err значение NSError, описывающее ошибку. (Перенаправление указателя таким образом иногда называют косвенным обращением.)

Таким образом, следующий шаг после вызова этого метода и сохранения результата — проверить равенство результата nil, просто чтобы убедиться, что действительно получен экземпляр строки. Если результат не nil, все хорошо; это и есть та строка, которую вы хотели получить. Но если результат равен nil, то надо исследовать NSError, чтобы узнать, что пошло неправильно.

NSString* path -   //            Какое-то имя
NSStringEncoding enc - II Какое-то значение
NSError* err - nil;
NSString* result -
[NSString stringWithContentsOfFile: path encoding: enc error: &err];
if (nil — result) ( // Ой! Что-то пошло не так...
//Из err можно узнать, что именно случилось
}

Этот шаблон часто используется в каркасе Cocoa. Не поймите его неправильно! Очень распространенная ошибка начинающих программистов — вызов метода stringWithCont entsOfFile:encoding:error: с немедленной проверкой значения переменной ошибки (в данном случае — err). Не делайте этого! Если ошибки не было, значение переменной err никак не гарантируется. Вместо этого начните с проверки результата (в данном случае — переменной result). И только если результат показывает, что произошла ошибка, то вот тогда и только тогда вы должны исследовать значение NSError, которое было установлено путем косвенного обращения.

 

В исходном тексте на чистом С иногда приходится встречаться с “указателем на ничто", выраженным как NULL. Функционально NULL и nil эквивалентны, так что не стесняйтесь везде использовать только nil.


Похожие статьи

 

 

 

 
 

У вас нет прав оставлять комментарии. Зарегистрируйтесь на сайте.