상수는 변수와 반대되는 의미로 고정된 값을 가졌음을 뜻한다.
Objective-C 에서 상수를 사용하기 위한 방법으로는 const ,  static const ,  extern const ,  #define , enum(NS_ENUM, NS_OPTIONS) 등이 있다. 

상수(const) 범위(scope)

우선 const 부터 살펴보자.
const 는 선언 시, 단독으로 사용할 지, static 또는 extern 과 함께 사용할 지에 따라 해당 상수의 선언 및 사용 범위(scope)가 달라지게 된다.

TestClass.h
@interface TestClass : NSObject // 아래처럼 단순히 const 선언은 property 로 사용 가능, static 과 extern은 선언 불가 // 그런데 이 방식은 _propertyName 처럼 인스턴스 변수에 직접 접근할 때는 상수로 동작하지만 // setter 메소드로 접근했을 때 상수가 아닌 변수처럼 동작하게 되므로 사용하지 않는다. @property NSString *const constStringProperty; - (void)someMethod1; @end //단순 const 는 이 곳에서 선언 불가 //NSString *const constString = @"const"; //static 과 extern은 선언 가능 static NSString *const staticConstString = @"Static Const String"; // extern은 헤더에서 바로 초기화 할 수 없다. extern NSString *const externConstString;
TestClass.m

헤더 파일에서 static const는 초기화해두었으므로 const와 extern const에 대하여 초기화

NSString *const constString = @"Const String"; NSString *const externConstString = @"Extern Const String"; @implementation TestClass - (void)someMethod1 { NSLog(@"%@, %p", constString, constString); NSLog(@"%@, %p", staticConstString, staticConstString); NSLog(@"%@, %p", externConstString, externConstString); } @end
main.m

constString은 header 에 선언하지 않았으므로 static과 extern에 대해서만 출력

int main(int argc, const char * argv[]) { TestClass *test = [TestClass new]; [test someMethod1]; NSLog(@"static : %@, %p", staticConstString, staticConstString); NSLog(@"extern : %@, %p", externConstString, externConstString); return 0; }
결과
Const String, 0x100001050 Static Const String, 0x100001070 Extern Const String, 0x100001090 static : Static Const String, 0x100001070 extern : Extern Const String, 0x100001090


위에서 살펴보듯이 const 는 아래와 같이 구분할 수 있다.
[ const ] 
클래스 내부에서만 사용할 전역상수로 사용할 수 있다. 하지만 const만 사용해서 선언하는 일은 없어야 한다. 내부에서 사용할 전역상수 목적일 경우 반드시 static const를 사용해야 한다. 변수가 static으로 선언되지 않으면 컴파일러는 해당 변수를 외부 심벌(external symbol)을 생성하게 되고 다른 파일에서 같은 이름의 변수를 선언하면 링커에서 에러를 일으킨다.
[ static const ]
static const를 결과 내용에서 보듯이 헤더파일에 선언한다면 해당 헤더를 import한 곳에서 공용으로 사용 가능하지만, 구현부에 정의 후 클래스 내부에서 사용할 전역상수로 사용하는 것이 좋다.
왜냐하면 해당 헤더를 #import 한 파일에서 static const 로 선언한 상수와 동일한 변수명을 정의했을 경우 #import 된 static const 값은 무시하고 재정의한 변수명으로 사용되기 때문이다.
[ extern const ]
클래스 외부에서 공용으로 사용할 전역상수로 사용.
static과 다르게 헤더가 아닌 구현부에서만 초기화가 가능하며, 선언부와 동일한 클래스가 아닌 다른 클래스에서 초기화를 할 수도 있으나 찾기 어려워지므로 동일 클래스의 구현부에 정의하는 것이 좋다.
그리고 전역상수의 이름은 연관된 클래스 이름을 접두어로 사용하는 것이 권장된다.

static const 선언 예제
static NSString *const MyThingNotificationKey = @"MyThingNotificationKey";
extern const 선언 예제
//.h extern NSString *const MyThingNotificationKey; //.m NSString *const MyThingNotificationKey = @"MyThingNotificationKey";
#define

#define은 컴파일 단계에서 미리 정해둔 문자열을 다른 문자열로 바로 치환시키는 형태로 동작한다.
즉 아래와 같이 define을 선언했을 경우

#define NAVER_URL @"naver.com"

프로그램을 실행하기 전에 #define 문은 컴파일러에 의해 NAVER_URL 이 @"naver.com" 치환되어 처음부터 @"naver.com" 이라고 입력한 것처럼 인식을 하게 된다.
C언어에서 상수를 사용하기 위해 많이 사용하던 방식이지만 Objective-C에서는 권장되지 않는 방식.

#define 을 권장하지 않는 이유

일반적으로 많이 인용되던 대답은 "define 매크로는 타입을 체크할 수 없어 런타임 오류가 발생할 수 있기 때문" 이었지만 최근엔 컴파일러가 많이 발전되어 대부분의 타입을 추론하여 사용할 수 있게 되었다.
하지만 이것만은 아니다.
1. static NSString *const variable  과 같은 선언은 그 자체만으로도 좋은 문서 역할을 한다.
2. #define 은 상수 선언이 아닌 단순 대치일 뿐이므로 중간에 실수로라도 변경이 될 위험이 있다.
2. 디버거는 #define macro에 대해 인식하지 못한다. 따라서 디버깅 중 define을 이용해 선언한 매크로키를 이용할 수 없다.

NS_ENUM, NS_OPTIONS

NS_ENUM 과 NS_OPTIONS 은 공용 또는 클래스 내부에서 상수로 사용하기 위해 많이 사용되는 방식 중 하나이다. 다만, const 나 define 방식과 큰 차이가 하나 있는데 NS_ENUM 과 OPTIONS는 결국 정수값만 정의하고 사용할 수 있기 때문이다.

typedef NS_ENUM(NSUInteger, testEnum){ someEnumString, // 0 someEnumDictionary, // 1 };

즉, 위와 같이 내부적으로 someEnumString, someEnumDictionary 같이 표현하더라도 실제 값은 정수값을 가진다. 따라서 정수값만 가져도 좋을 상수인 경우는 상관없으나 그렇지 않을 때는 다른 방식을 사용해야 한다는 한계점이 있다.

참조링크