#define vs const в Objective-C

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

Сначала я обнаружил, что невозможно определить тип константы, используя #define. Почему это?

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

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

Ответ 1

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

Почему? Это не так:

#define MY_INT_CONSTANT ((int) 12345)

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

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

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

Просто подумал о контексте, в котором вы можете использовать #define, но не const. Если у вас есть константа, которую вы хотите использовать в большом количестве файлов .c, с #define вы просто вставляете ее в заголовок. С помощью 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 делает почти неизменно. Это имеет все преимущества как #define, так и const, но работает только для целочисленных констант.

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

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

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

#define FOO 5

// ....

FOO = 6;   // Always a syntax error

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

const int FOO = 5;

// ...

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

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

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

Ответ 2

От C-кодера:

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

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

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

Ответ 3

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

const

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

#define

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

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

Ответ 4

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

Ответ 5

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

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

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

DEFINE_INT(x,42) 

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

const int x = 42;

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

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

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

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

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

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

Ответ 6

Я использовал #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)

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

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

.

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