2008-10-01 2 views
101

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

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

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

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

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

+2

У меня также был фона Java, и я был в ужасе от этой приятной функции в начале, но теперь я нахожу ее абсолютно ЛЮБИМЧИВОЙ !; – 2011-08-23 22:18:50

+1

Спасибо, это отличный вопрос. Вы видели, чтобы увидеть преимущества этого? Это поражает меня как «не ошибку, особенность». Я продолжаю получать ошибки, когда Java просто ударит меня с исключением, поэтому я знал, где проблема.Я не рад обменять исключение нулевого указателя, чтобы сохранить строку или два тривиального кода здесь и там. – 2012-09-18 17:43:20

ответ

89

Ну, я думаю, это можно описать, используя очень надуманный пример. Скажем, у вас есть метод в 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), если передается nil NSString.

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

+32

Возможно, стоит упомянуть, что это поведение было предметом многочисленных дискуссий в сообществе Objective-C за последние пару десятилетий. Компромисс между «безопасностью» и «удобством» по-разному оценивается разными людьми. – 2008-10-02 19:17:01

+3

На практике существует много симметрии между передачей сообщений в nil и тем, как работает Objective-C, особенно в новой функции слабых указателей в ARC. Слабые указатели автоматически обнуляются. Таким образом, вы проектируете API так, чтобы он мог отвечать на 0/nil/NIL/NULL и т. Д. – Cthutu 2012-03-21 18:28:49

+1

Я думаю, что если вы выполните `myObject-> iVar`, он сработает, независимо от того, является ли это объектами C_with_ или _without_. (извините gravedig.) – 11684 2013-03-06 14:03:12

16

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

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

[someNullNSArrayReference count] => 0 

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

9

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

[someVariable release]; 

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

if ([myString length] > 0) 

или это:

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

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

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

12

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

Есть несколько моделей в какао, которые используют преимущества этого факта.

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

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

50

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

38

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

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

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

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

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

6

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

6

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

Я опубликовал аналогичный, но более длинный ответ на соответствующий вопрос "Is asserting that every object creation succeeded necessary in Objective C?", если вы хотите получить более подробную информацию.

11

От Greg Parker «ы site:

Если запуск LLVM компилятор 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} 
2

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

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

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

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

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

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

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

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

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

Смежные вопросы