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