#define vs const в Objective-C

81

Я новичок в Objective-C, и у меня есть несколько вопросов относительно constдирективы предварительной обработки #define.

Во-первых, я обнаружил, что невозможно определить тип константы с помощью #define. Это почему?

Во-вторых, есть ли преимущества в использовании одного из них перед другим?

Наконец, какой способ более эффективен и / или безопаснее?

ObjectiveCoder
источник

Ответы:

107

Во-первых, я обнаружил, что невозможно определить тип константы с помощью #define, почему?

Почему что? Это неправда:

#define MY_INT_CONSTANT ((int) 12345)

Во-вторых, есть ли преимущества в использовании одного из них перед другим?

Да. #defineопределяет макрос, который заменяется еще до начала компиляции. constпросто изменяет переменную, чтобы компилятор пометил ошибку, если вы попытаетесь ее изменить. Есть контексты, в которых вы можете использовать a, #defineно не можете использовать const(хотя я изо всех сил пытаюсь найти его, используя последнюю версию clang). Теоретически a constзанимает место в исполняемом файле и требует ссылки на память, но на практике это несущественно и может быть оптимизировано компилятором.

consts гораздо более дружелюбны к компилятору и отладчику, чем #defines. В большинстве случаев это главный момент, который вы должны учитывать при принятии решения, какой из них использовать.

Просто подумайте о контексте, в котором вы можете использовать, #defineно не можете const. Если у вас есть константа, которую вы хотите использовать во многих .cфайлах, #defineвы просто вставляете ее в заголовок. С a у constвас должно быть определение в файле C и

// in a C file
const int MY_INT_CONST = 12345;

// in a header
extern const int MY_INT_CONST;

в шапке. MY_INT_CONSTне может использоваться как размер статического или глобального массива области видимости в любом файле C, кроме того, в котором он определен.

Однако для целочисленных констант вы можете использовать расширение enum. Фактически, это то, что Apple делает почти всегда. Это имеет все преимущества как #defines, так и consts, но работает только для целочисленных констант.

// In a header
enum
{
    MY_INT_CONST = 12345,
};

Наконец, какой способ более эффективен и / или безопаснее?

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

#define FOO 5

// ....

FOO = 6;   // Always a syntax error

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

const int FOO = 5;

// ...

(int) FOO = 6;     // Can make this compile

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

Лично для целочисленных констант я всегда использую enums для констант других типов, я использую, constесли у меня нет очень веской причины не делать этого.

JeremyP
источник
Я знаю, что он старый, но один случай, когда вы не можете использовать константу, которую можно было бы использовать для определения, - это "#define MY_NSNUM_CONSTANT @ 5", что невозможно сделать с помощью "NSNumber * const MY_NSNUM_CONSTANT = @ 5"
shortstuffsushi
Я бы сказал, что #define занимает больше места в исполняемом файле, чем const. Константа сохраняется один раз, но #define умножается каждый раз, когда вы ее используете, потому что это просто замена текста. Но так как разница невелика, я излишне педантичен.
Райан Баллантайн
@RyanBallantyne Я думаю, вы могли бы быть правы для таких вещей, как строковые константы, но не для целочисленных констант, потому что даже вы храните константу в одном месте, для доступа к ней вам нужен ее адрес, который по крайней мере такой же большой, как int. Однако я был бы очень удивлен, если бы это вообще имело значение в современном компиляторе.
JeremyP
16

Из кодера C:

A const- это просто переменная, содержимое которой нельзя изменить.

#define name valueОднако, это команда препроцессора , которая заменяет все экземпляры nameс value.

Например, если вы #define defTest 5, все экземпляры defTestв вашем коде будут заменены на 5.

MegaNairda
источник
11

Важно понимать разницу между инструкциями #define и const, которые не предназначены для одного и того же.

const

constиспользуется для создания объекта из запрошенного типа, который после инициализации будет постоянным. Это означает, что это объект в памяти программы и может использоваться только для чтения. Объект генерируется каждый раз при запуске программы.

#define

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

Если позже вы решите изменить значение ширины, вам нужно будет изменить его только в определении вместо утомительного и опасного поиска / замены во всем файле. При компиляции препроцессор заменит все заданное имя значениями в коде. Следовательно, вы не теряете время, используя их.

fБуржуазный
источник
7

В дополнение к комментариям других людей, ошибки с использованием #defineобщеизвестно сложно отлаживать, поскольку препроцессор получает их до компилятора.

Эд Хил
источник
3

Поскольку директивы препроцессора не одобряются, я предлагаю использовать const. Вы не можете указать тип с препроцессором, потому что директива препроцессора разрешается перед компиляцией. Что ж, можно, но примерно так:

#define DEFINE_INT(name,value) const int name = value;

и использовать его как

DEFINE_INT(x,42) 

который будет рассматриваться компилятором как

const int x = 42;

Во-первых, я обнаружил, что невозможно определить тип константы с помощью #define, почему?

Вы можете посмотреть мой первый фрагмент.

Во-вторых, есть ли преимущества в использовании одного из них перед другим?

Обычно наличие constдирективы вместо препроцессора помогает при отладке, но не так сильно в этом случае (но все же помогает).

Наконец, какой способ более эффективен и / или безопаснее?

Оба они столь же эффективны. Я бы сказал, что макрос потенциально может быть более безопасным, поскольку его нельзя изменить во время выполнения, тогда как переменную можно.

Лучиан Григоре
источник
Зачем просто вводить const ...вместо макроса?
Эд Хил
1
@EdHeal не надо. Я просто сказал, что вы можете в ответ на вопрос: «Я обнаружил, что невозможно определить тип константы с помощью #define, почему это так?»
Лучиан Григоре
1
> pre-processor directives are frowned upon[необходима цитата]
Бен Легжеро
Я думаю, вы можете использовать такой тип: #define myValue ((double) 2). Насколько я понимаю, препроцессор просто заменит myValue тем, что идет после него в операторе define, включая информацию о типе.
vomako
1

Я использовал #define раньше, чтобы создать больше методов из одного метода, например, если у меня есть что-то вроде.

 // This method takes up to 4 numbers, we don't care what the method does with these numbers.
 void doSomeCalculationWithMultipleNumbers:(NSNumber *)num1 Number2:(NSNumber *)num2 Number3:(NSNumber *)num23 Number3:(NSNumber *)num3;

Но мне также нужно иметь метод, который принимает только 3 числа и 2 числа, поэтому вместо написания двух новых методов я собираюсь использовать тот же самый, используя #define, вот так.

 #define doCalculationWithFourNumbers(num1, num2, num3, num4) \
         doSomeCalculationWithMultipleNumbers((num1), (num2), (num3), (num4))

 #define doCalculationWithThreeNumbers(num1, num2, num3) \
         doSomeCalculationWithMultipleNumbers((num1), (num2), (num3), nil)

 #define doCalculationWithTwoNumbers(num1, num2) \
         doSomeCalculationWithMultipleNumbers((num1), (num2), nil, nil)

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

     NSLocalizedString(<#key#>, <#comment#>)
     NSLocalizedStringFromTable(<#key#>, <#tbl#>, <#comment#>)
     NSLocalizedStringFromTableInBundle(<#key#>, <#tbl#>, <#bundle#>, <#comment#>)

сделано.

Хотя я не верю, что вы можете сделать это с помощью констант. Но у констант есть преимущества перед #define, например, вы не можете указать тип с помощью #define, потому что это директива препроцессора, которая разрешается перед компиляцией, и если вы получите ошибку с помощью #define, их сложнее отладить, тогда константы. У обоих есть свои преимущества и недостатки, но я бы сказал, все зависит от программиста, которого вы решили использовать. Я написал библиотеку с ними обоими, используя #define, чтобы делать то, что я показал, и константы для объявления постоянных переменных, для которых мне нужно указать тип.

Попай
источник