Jaki jest najlepszy sposób tworzenia stałych w Objective-C

156

Tworzę klienta Reddit do celów edukacyjnych. Muszę mieć plik ze stałymi. Myślałem o zaimportowaniu pliku do Reddit-Prefix.pchpliku, aby stałe były dostępne dla wszystkich plików. Czy to dobry sposób na robienie rzeczy? Ponadto przeprowadziłem badania i znalazłem kilka metod tworzenia stałych, ale nie wiem, której użyć:

  • #define makro
  • const
  • static const
  • extern const
  • enum

Więc który sposób jest preferowany? Jaka jest konwencja? Wiem, że „to zależy”, ale moje pytanie jest bardziej szczegółowe: Jakie są przypadki użycia każdego z tych rozwiązań?

Ponadto, jeśli używam extern const, czy muszę importować plik, czy też stałe będą dostępne globalnie bez importowania pliku?

Jedna rzecz, którą mogę logicznie wywnioskować, enumto najlepszy wybór przy definiowaniu czegoś takiego jak niestandardowe domeny błędów (czy rzeczywiście mam rację?). A co z innymi?

Robert Audi
źródło
stackoverflow.com/questions/11153156/ ... odwiedź ten link ... Twoje rozwiązanie jest w tym poście
Użytkownik 1531343
3
@BhavikKama: To węższe pytanie, zestawiające dwa konkretne rozwiązania.
Peter Hosey,
for - static const, #define, enum, ten link jest przydatny stackoverflow.com/questions/1674032/static-const-vs-define-in-c, który zapewnia dobre wyjaśnienie tych 3 alternatyw dla const
Użytkownik 1531343
enumjest użyteczne tylko dla wartości całkowitych. #definea stałe mogą być dowolnym typem danych.
rmaddy,
const, static consti extern constsą takie same z wyjątkiem zakresu. Więc tak naprawdę są tylko trzy możliwości.
rmaddy

Odpowiedzi:

385

Pierwsze pytanie dotyczy zakresu, w jakim chcesz mieć swoje stałe, czyli tak naprawdę dwa pytania:

  • Czy te stałe są specyficzne dla jednej klasy, czy też ma sens umieszczanie ich w całej aplikacji?
  • Jeśli są specyficzne dla klasy, czy są przeznaczone do użytku przez klientów klasy, czy tylko w ramach klasy?

Jeśli są specyficzne i wewnętrzne dla jednej klasy, zadeklaruj je jako static constna początku pliku .m, na przykład:

static NSString *const MyThingNotificationKey = @"MyThingNotificationKey";

Jeśli odnoszą się do pojedynczej klasy, ale powinny być publiczne / używane przez inne klasy, zadeklaruj je tak, jak externw nagłówku i zdefiniuj je w .m:

//.h
extern NSString *const MyThingNotificationKey;

//.m
NSString *const MyThingNotificationKey = @"MyThingNotificationKey";

Jeśli powinny być globalne, zadeklaruj je w nagłówku i zdefiniuj w odpowiednim module, szczególnie dla tych stałych.

Możesz je łączyć i dopasowywać dla różnych stałych o różnych poziomach globalności, które chcesz, aby były, oraz dla różnych stałych globalnych, które po prostu nie pasują do siebie - możesz umieścić je w osobnych modułach, każdy z własnym nagłówkiem, jeśli chcieć.

Dlaczego nie #define?

Stara odpowiedź brzmi: „makra nie mają informacji o typie”, ale dzisiejsze kompilatory są całkiem sprytne, jeśli chodzi o sprawdzanie typów literałów (do jakich makr rozwijają się), a także zmiennych.

Nowoczesna odpowiedź brzmi, ponieważ debugger nie będzie wiedział o twoich makrach. W [myThing addObserver:self forKey:MyThingNotificationKey]poleceniu debugera nie można powiedzieć, czy MyThingNotificationKeyjest to makro; debugger może o tym wiedzieć tylko wtedy, gdy jest to zmienna.

Dlaczego nie enum?

Cóż, rmaddy pobił mnie w komentarzach: enummoże definiować tylko stałe całkowite. Rzeczy takie jak numery identyfikatorów seryjnych, maski bitowe, kody czterobajtowe itp.

Do tych celów enumjest świetny i absolutnie powinieneś go używać. (Nawet lepiej wykorzystywać te NS_ENUMi NS_OPTIONSmakra ). W przypadku innych rzeczy, to trzeba użyć czegoś innego; enumnie robi nic poza liczbami całkowitymi.

I inne pytania

Myślałem o zaimportowaniu pliku w pliku Reddit-Prefix.pch, aby stałe były dostępne dla wszystkich plików. Czy to dobry sposób na robienie rzeczy?

Prawdopodobnie nieszkodliwe, ale prawdopodobnie przesadne. Zaimportuj swoje nagłówki stałych tam, gdzie ich potrzebujesz.

Jakie są przypadki użycia każdego z tych rozwiązań?

  • #define: Dość ograniczone. Szczerze mówiąc, nie jestem pewien, czy jest już dobry powód, aby używać tego do stałych.
  • const: Najlepsze dla stałych lokalnych. Musisz także użyć tego dla tego, który zadeklarowałeś w nagłówku i teraz definiujesz.
  • static const: Najlepsze dla stałych specyficznych dla plików (lub klas).
  • extern const: Musisz tego użyć podczas eksportowania stałej w nagłówku.

Ponadto, jeśli używam extern const, czy muszę importować plik, czy też stałe będą dostępne globalnie bez importowania pliku?

Musisz zaimportować plik w każdym pliku, w którym go używasz, lub w nagłówku prefiksu.

Peter Hosey
źródło
3
Dlaczego nie używać static NSString *constw .hpliku w ogóle?
Iulian Onofrei
3
@IulianOnofrei: Możesz, jeśli jest to aplikacja, a nie framework. Jeśli to zrobisz static NSString *const foo = @"foo";, to twój nagłówek określa, co to jest ciąg i musi być wszędzie taki sam - jeśli kiedykolwiek zmienisz ciąg i różne strony używają różnych wersji nagłówka z innym ciągiem, wówczas ciągi nie będą pasować podczas wykonywania czas. We frameworku chcesz zapewnić dostęp tylko do symbolu i pozwolić, aby struktura była jedynym źródłem prawdziwej wartości tego symbolu, aby każdy mógł uzyskać ten sam ciąg z jednego miejsca. To właśnie externcię dostaje.
Peter Hosey
Dodatkowa uwaga na temat #defines: nie ma gwarancji, że mają ten sam adres w pamięci (w zależności od tego, jak są zadeklarowane, mogą przydzielać nową instancję za każdym razem, gdy są używane), więc używanie myObject == MyDefinenie zawsze będzie działać zgodnie z oczekiwaniami, ale myObject == MyStaticConstbędzie.
Ben Leggiero
Czy to ma sens w pisowni jak static NSString *constzamiast static NSString const*?? Jakieś różnice ?!
kokos8998
@ kokos8998 Czy to robi różnicę? Tak. static NSString const *jest tym samym co static const NSString *i oznacza „(zmienny) wskaźnik do stałego NSString” - co jest tutaj trochę bezużyteczne, ponieważ NSString jest już niezmienny. Tylko static NSString * constto, czego chcesz - czyli „stały wskaźnik do NSString”
David,
8

FOUNDATION_EXPORT

Rozważ użycie, FOUNDATION_EXPORTaby uzyskać nieco większą kompatybilność, niż externponieważ jest zdefiniowana w fundamencie i kompiluje do kompatybilnych formatów dla C, C ++ i Win32.

Zgodnie z definicją w NSObjCRuntime.h

#if defined(__cplusplus)
#define FOUNDATION_EXTERN extern "C"
#else
#define FOUNDATION_EXTERN extern
#endif

#if TARGET_OS_WIN32

    #if defined(NSBUILDINGFOUNDATION)
        #define FOUNDATION_EXPORT FOUNDATION_EXTERN __declspec(dllexport)
    #else
        #define FOUNDATION_EXPORT FOUNDATION_EXTERN __declspec(dllimport)
    #endif

    #define FOUNDATION_IMPORT FOUNDATION_EXTERN __declspec(dllimport)

#else
    #define FOUNDATION_EXPORT  FOUNDATION_EXTERN
    #define FOUNDATION_IMPORT FOUNDATION_EXTERN
#endif
Steve Moser
źródło