#define vs const у Objective-C


81

Я новачок у Objective-C, і у мене є кілька запитань щодо constдирективи попередньої обробки #define.

По-перше, я виявив, що неможливо визначити тип використання константи #define. Чому так?

По-друге, чи є якісь переваги використання одного з них перед іншим?

Нарешті, який шлях є більш ефективним та / або більш безпечним?

Відповіді:


107

По-перше, я виявив, що неможливо визначити тип константи за допомогою #define, чому це?

Чому це що? Це неправда:

#define MY_INT_CONSTANT ((int) 12345)

По-друге, чи є якісь переваги використання одного з них перед іншим?

Так. #defineвизначає макрос, який замінюється ще до початку компіляції. constпросто модифікує змінну, щоб компілятор позначив помилку, якщо ви спробуєте її змінити. Є контексти, в яких ти можеш використовувати a, #defineале ти не можеш використовувати a const(хоча я намагаюся знайти його, використовуючи останній дзвін). Теоретично 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якщо не маю поважних причин цього не робити.


Я знаю, що він старий, але один приклад, коли ви не можете використовувати const, який ви можете використовувати для визначення, - "#define MY_NSNUM_CONSTANT @ 5", що неможливо зробити за допомогою "NSNumber * const MY_NSNUM_CONSTANT = @ 5"
shortstuffsushi

Я б стверджував, що #define займає більше місця у виконуваному файлі, ніж const. Const зберігається один раз, але #define множиться кожного разу, коли ви використовуєте його, оскільки це просто заміна тексту. Але оскільки різниця незначна, то я надмірно педантичний.
Райан Баллантайн

@RyanBallantyne Я думаю, ти можеш мати рацію щодо таких речей, як рядкові константи, але не для цілочисельних констант, тому що навіть якщо ти зберігаєш константу в одному місці, для доступу до неї потрібна її адреса, щонайменше велика як int. Однак я був би дуже здивований, якби це взагалі щось змінило в сучасному компіляторі.
JeremyP

16

З кодера C:

A const- це просто змінна, вміст якої неможливо змінити.

#define name valueоднак є командою препроцесора, яка замінює всі екземпляри nameз value.

Наприклад, якщо ви #define defTest 5, усі екземпляри defTestвашого коду будуть замінені на, 5коли ви компілюєте.


11

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

const

constвикористовується для створення об'єкта із запитуваного типу, який після ініціалізації буде постійним. Це означає, що він є об'єктом в пам'яті програми і може використовуватися лише для читання. Об'єкт генерується кожного разу при запуску програми.

#define

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

Якщо пізніше ви вирішите змінити значення ширини, вам доведеться змінити його лише у дефініції замість нудної та небезпечної пошуку / заміни у цілому вашому файлі. Під час компіляції препроцесор замінить усі визначені імена значеннями в коді. Отже, час їх використання не втрачається.


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, чому це так?"
Luchian Grigore

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)

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

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

готові.

Тоді як я не вірю, що ви можете зробити це за допомогою констант. Але константи мають переваги перед #define, наприклад, ви не можете вказати тип за допомогою #define, оскільки це директива попереднього процесора, яка вирішується перед компіляцією, і якщо ви отримуєте помилку з #define, їх важче налагодити, тоді константи. У обох є свої переваги та недоліки, але я б сказав, що все залежить від програміста, до якого ви вирішили скористатися. Я написав бібліотеку як з ними, використовуючи #define, щоб робити те, що я показав, так і константи для оголошення констант змінних, для яких мені потрібно вказати тип.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.