Отправка сообщения на нуль в Objective-C

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

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

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

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

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

Ответ 1

Ну, я думаю, это можно описать с помощью очень надуманного примера. Скажем, у вас есть метод в 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 - это функция, а не метод, она выводит (null), если передана niltnring.

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

Ответ 2

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

Ответ 3

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

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

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

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

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

Ответ 4

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

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

[someNullNSArrayReference count] => 0

I.e., nil представляется пустым массивом. Скрытие нисходящей ссылки NSView ничего не делает. Handy, eh?

Ответ 5

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

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

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

Вероятнее всего, первое здесь имеет смысл: обычно отправка сообщений в 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.

Ответ 6

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

Если выполняется 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}

Ответ 7

Это означает, что часто не нужно проверять наличие нулевых объектов везде для безопасности - в частности:

[someVariable release];

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

if ( [myString length] > 0 )

или это:

return [myArray count]; // say for number of rows in a table

Ответ 8

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

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

Ответ 9

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

Ответ 10

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

Я написал аналогичный, но более длинный ответ на соответствующий вопрос "Утверждает, что каждое создание объекта было необходимо в Objective C?" , если вы хотите больше подробности.

Ответ 11

C не представляет ничего как 0 для примитивных значений и NULL для указателей (что эквивалентно 0 в контексте указателя).

Objective-C строит на C представление ничего, добавляя nil. nil - это указатель на объект. Хотя они семантически отличаются от NULL, они технически эквивалентны друг другу.

New-alloc'd NSObjects начинает свою жизнь с установленного значения 0. Это означает, что все указатели, которые объект имеет для других объектов, начинаются с нуля, поэтому нет необходимости, например, устанавливать self. (association) = nil in init.

Самое примечательное поведение nil, однако, состоит в том, что он может отправлять сообщения на него.

В других языках, таких как С++ (или Java), это приведет к сбою вашей программы, но в Objective-C, вызов метода на nil возвращает нулевое значение. Это значительно упрощает выражения, поскольку оно устраняет необходимость проверять значение nil перед тем, как делать что-либо:

// For example, this expression...
if (name != nil && [name isEqualToString:@"Steve"]) { ... }

// ...can be simplified to:
if ([name isEqualToString:@"Steve"]) { ... }

Знание того, как работает nil в Objective-C, позволяет этому удобству быть функцией, а не скрывающейся ошибкой в ​​вашем приложении. Удостоверьтесь, что вы защищаете от случаев, когда значения nil нежелательны, либо путем проверки, либо возврата на ранней стадии, что они терпят неудачу, или добавления NSParameterAssert для исключения.

Источник: http://nshipster.com/nil/ https://developer.apple.com/library/ios/#documentation/cocoa/conceptual/objectivec/Chapters/ocObjectsClasses.html (Отправка сообщения на нуль).