it-swarm.com.ru

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

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

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

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

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

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

104
Ryan Delucchi

Ну, я думаю, что это можно описать, используя очень надуманный пример. Допустим, у вас есть метод в 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/нулевую структуру, то это избавляет вас от необходимости проверять, чтобы убедиться, что объект или параметры равны нулю.

92
Michael Buckley

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

51
Peter Hosey

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

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

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

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

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

40
Joe McMahon

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

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

[someNullNSArrayReference count] => 0

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

16
Rich

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

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

Значение, возвращаемое из сообщения в 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.

12
mmalc

От Грег Паркер --- сайт :

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

 Сообщения на ноль с типом возврата | вернуть 
 целые числа до 64 бит | 0 
 С плавающей точкой до длинных двойных | 0.0 
 Указатели | ноль 
 Структуры | {0} 
 Любой _Сложный тип | {0, 0} 
12
Heath Borders

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

[someVariable release];

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

if ( [myString length] > 0 )

или это:

return [myArray count]; // say for number of rows in a table
9
Kendall Helmstetter Gelner

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

6
Nikita Zhuk

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

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

6
benzado

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

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

6
Rinzwind

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

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

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

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

В других языках, таких как C++ (или 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 (отправка сообщения на ноль).

2
Zee