Отправка сообщения на ноль в Objective-C - PullRequest
103 голосов
/ 01 октября 2008

Как Java-разработчик, который читает документацию Apple Objective-C 2.0: мне интересно, что означает " отправка сообщения на nil " - не говоря уже о том, как это действительно полезно. Взятие выдержки из документации:

Есть несколько моделей в какао что воспользоваться этим фактом. значение, возвращаемое из сообщения в ноль также может быть действительным:

  • Если метод возвращает объект, любой тип указателя, любой целочисленный скаляр размером меньше или равно sizeof (void *), поплавок, двойной, длинный двойной или длинный длинный, то сообщение, отправленное на ноль, возвращает 0.
  • Если метод возвращает структуру, как определено в ABI-функции Mac OS X Руководство по вызову будет возвращено в регистрируется, затем сообщение отправляется на ноль возвращает 0.0 для каждого поля в структура данных. Другие данные структуры типы не будут заполнены нулями.
  • Если метод возвращает что-либо кроме указанного выше значения набирает возвращаемое значение сообщения отправлено на ноль не определено.

Сделала ли Ява моим мозгом неспособность уловить объяснение выше? Или что-то, чего мне не хватает, сделает это ясным, как стекло?

Я понял идею сообщений / получателей в Objective-C, я просто запутался в том, что получатель nil.

Ответы [ 11 ]

91 голосов
/ 01 октября 2008

Ну, я думаю, это можно описать, используя очень надуманный пример. Допустим, у вас есть метод в Java, который распечатывает все элементы в ArrayList:

void foo(ArrayList list)
{
    for(int i = 0; i < list.size(); ++i){
        System.out.println(list.get(i).toString());
    }
}

Теперь, если вы вызываете этот метод следующим образом: someObject.foo (NULL); вы, вероятно, получите исключение NullPointerException при попытке доступа к списку, в данном случае при вызове list.size (); Теперь вы, вероятно, никогда бы не вызвали someObject.foo (NULL) с таким значением NULL. Однако вы, возможно, получили свой ArrayList от метода, который возвращает NULL, если он сталкивается с некоторой ошибкой, генерирующей ArrayList, например someObject.foo (otherObject.getArrayList ());

Конечно, у вас также будут проблемы, если вы сделаете что-то вроде этого:

ArrayList list = NULL;
list.size();

Теперь в Objective-C у нас есть эквивалентный метод:

- (void)foo:(NSArray*)anArray
{
    int i;
    for(i = 0; i < [anArray count]; ++i){
        NSLog(@"%@", [[anArray objectAtIndex:i] stringValue];
    }
}

Теперь, если у нас есть следующий код:

[someObject foo:nil];

у нас такая же ситуация, когда Java создаст исключение NullPointerException. Сначала к объекту nil будет получен доступ в [anArray count]. Однако, вместо того, чтобы выдавать исключение NullPointerException, Objective-C просто вернет 0 в соответствии с приведенными выше правилами, поэтому цикл не будет выполняться. Однако, если мы установим цикл на выполнение заданного числа раз, то сначала мы отправим сообщение в anArray в [anArray objectAtIndex: i]; Это также вернет 0, но поскольку objectAtIndex: возвращает указатель, а указатель на 0 равен nil / NULL, NSLog будет пропущен nil каждый раз через цикл. (Хотя NSLog является функцией, а не методом, он выводит (ноль), если передан ноль NSString.

В некоторых случаях лучше иметь исключение NullPointerException, поскольку вы можете сразу сказать, что с программой что-то не так, но если вы не уловите исключение, программа завершится сбоем. (В C попытка разыменования NULL таким способом приводит к сбою программы.) В Objective-C вместо этого это просто вызывает, возможно, некорректное поведение во время выполнения. Однако, если у вас есть метод, который не прерывается, если он возвращает 0 / nil / NULL / нулевую структуру, то это избавляет вас от необходимости проверять, чтобы убедиться, что объект или параметры равны nil.

51 голосов
/ 10 ноября 2008

Сообщение nil ничего не делает и возвращает nil, Nil, NULL, 0 или 0.0.

40 голосов
/ 22 ноября 2008

Все остальные сообщения верны, но, возможно, здесь важна концепция.

В вызовах метода Objective C любая ссылка на объект, которая может принять селектор, является допустимой целью для этого селектора.

Это сохраняет много "целевой объект типа X?" код - до тех пор, пока принимающий объект реализует селектор, он абсолютно не имеет значения какой это класс! nil - это объект NSO, который принимает любой селектор - он просто не делает ничего. Это исключает большую часть кода «проверьте на ноль, не отправляйте сообщение, если оно истинно». (Концепция «если он принимает это, он реализует ее» также позволяет создавать протоколы , которые в некотором роде похожи на интерфейсы Java: объявление о том, что если класс реализует указанные методы, то он соответствует к протоколу.)

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

Разъяснение для downvoters: вы можете подумать, что это не очень хороший путь, но именно так реализован язык, и это рекомендуемая идиома программирования в Objective-C (см. Лекции по программированию в Stanford для iPhone ).

16 голосов
/ 01 октября 2008

Это означает, что среда выполнения не выдает ошибку при вызове objc_msgSend для указателя nil; вместо этого он возвращает некоторое (часто полезное) значение. Сообщения, которые могут иметь побочный эффект, ничего не делают.

Это полезно, потому что большинство значений по умолчанию являются более подходящими, чем ошибка. Например:

[someNullNSArrayReference count] => 0

Т.е. nil представляется пустым массивом. Скрытие нулевой ссылки NSView ничего не делает. Удобно, а?

12 голосов
/ 01 марта 2012

Из Грег Паркер сайт :

Если работает LLVM Compiler 3.0 (Xcode 4.2) или новее

Messages to nil with return type | return
Integers up to 64 bits           | 0
Floating-point up to long double | 0.0
Pointers                         | nil
Structs                          | {0}
Any _Complex type                | {0, 0}
12 голосов
/ 13 октября 2008

В цитате из документации есть две отдельные концепции - возможно, было бы лучше, если бы в документации это было более понятно:

В Какао есть несколько шаблонов, которые используют этот факт.

Значение, возвращаемое из сообщения в ноль, также может быть действительным:

Первое здесь, вероятно, более уместно: обычно возможность отправлять сообщения на nil делает код более простым - вам не нужно везде проверять наличие нулевых значений. Каноническим примером, вероятно, является метод доступа:

- (void)setValue:(MyClass *)newValue {
    if (value != newValue) { 
        [value release];
        value = [newValue retain];
    }
}

Если бы отправка сообщений на nil была недействительной, этот метод был бы более сложным - вам нужно было бы провести две дополнительные проверки, чтобы убедиться, что value и newValue не являются nil перед отправкой им сообщений.

Последний пункт (что значения, возвращаемые из сообщения в nil, также обычно действительны), однако, добавляет эффект умножения к первому. Например:

if ([myArray count] > 0) {
    // do something...
}

Этот код снова не требует проверки значений nil и работает естественным образом ...

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

9 голосов
/ 01 октября 2008

Это часто означает, что нет необходимости проверять нигде объекты на предмет безопасности, особенно:

[someVariable release];

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

if ( [myString length] > 0 )

или это:

return [myArray count]; // say for number of rows in a table
6 голосов
/ 03 апреля 2011

Я не думаю, что в других ответах это четко упоминалось: если вы привыкли к Java, вы должны иметь в виду, что хотя Objective-C в Mac OS X имеет поддержку обработки исключений, это дополнительная языковая функция это может быть включено / выключено с флагом компилятора. Я предполагаю, что такой дизайн «отправки сообщений на nil является безопасным» предшествовал включению поддержки обработки исключений в языке и был сделан с аналогичной целью: методы могут возвращать nil для указания ошибок, а после отправки сообщение на nil обычно возвращает nil в свою очередь, это позволяет индикатору ошибки распространяться через ваш код, поэтому вам не нужно проверять его в каждом отдельном сообщении. Вы должны проверить это только в тех местах, где это имеет значение. Я лично считаю, что распространение и обработка исключений - лучший способ решения этой задачи, но не все могут с этим согласиться. (С другой стороны, мне, например, не нравится требование Java о том, что вы должны объявлять, какие исключения может вызывать метод, что часто вынуждает синтаксически распространять объявления исключений по всему коду; но это другое обсуждение .)

Я опубликовал аналогичный, но более длинный ответ на связанный вопрос «Утверждает ли, что каждое создание объекта успешно выполнено, необходимо в Задаче C?» , если вы хотите получить больше информации.

6 голосов
/ 15 марта 2009

Сообщения ObjC, которые отправляются на ноль и чьи возвращаемые значения имеют размер больше, чем sizeof (void *), создают неопределенные значения на процессорах PowerPC. Кроме того, эти сообщения приводят к тому, что неопределенные значения возвращаются в полях структур, размер которых также превышает 8 байтов на процессорах Intel. Винсент Гейбл хорошо описал это в своем блоге

6 голосов
/ 02 октября 2008

Не думайте, что "получатель ноль"; Я согласен, что довольно странно. Если вы отправляете сообщение на ноль, получателя нет. Вы просто отправляете сообщение на пустые места.

Как бороться с этим - философское различие между Java и Objective-C: в Java это ошибка; в Objective-C это не работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...