iOS 애플리케이션에서 전역 상수를 저장할 위치는?


111

내 iOS 앱의 대부분의 모델은 웹 서버를 쿼리합니다. 서버의 기본 URL을 저장하는 구성 파일을 갖고 싶습니다. 다음과 같이 보일 것입니다.

// production
// static NSString* const baseUrl = "http://website.com/"

// testing
static NSString* const baseUrl = "http://192.168.0.123/"

한 줄 또는 다른 줄을 주석 처리하여 모델이 가리키는 서버를 즉시 변경할 수 있습니다. 제 질문은 iOS에 전역 상수를 저장하는 가장 좋은 방법은 무엇입니까? Android 프로그래밍에는이 내장 문자열 리소스 파일이 있습니다. 모든 활동 ( UIViewController에 해당 )에서 다음을 사용하여 해당 문자열 상수를 검색 할 수 있습니다.

String string = this.getString(R.string.someConstant);

iOS SDK에 상수를 저장할 수있는 유사한 위치가 있는지 궁금합니다. 그렇지 않은 경우 Objective-C의 모범 사례는 무엇입니까?

답변:


145

당신은 또한 할 수 있습니다

#define kBaseURL @"http://192.168.0.123/"

"constants"헤더 파일에서 constants.h. 그런 다음

#include "constants.h"

이 상수가 필요한 모든 파일의 맨 위에 있습니다.

이렇게하면 다음과 같이 컴파일러 플래그에 따라 서버간에 전환 할 수 있습니다.

#ifdef DEBUG
    #define kBaseURL @"http://192.168.0.123/"
#else
    #define kBaseURL @"http://myproductionserver.com/"
#endif

내가 사용 "constants.h"선언, 접근 방식을 static기반으로 변수를 #ifdef VIEW_CONSTANTS ... #endif. 따라서 응용 프로그램 전체에 적용되는 상수 파일이 하나 있지만 다른 코드 파일 은 상수 파일을 작성 #define하기 전에 포함 할 서로 다른 상수 세트입니다 #include( "정의되었지만 사용되지 않은"컴파일러 경고를 모두 중지).

2
이 솔루션에는 두 가지 문제가 있습니다. 먼저를 사용할 때 #decalare" invalid preprocessing directive declare " 라는 컴파일 오류가 발생했습니다 . 그래서 #define대신 변경했습니다 . 다른 문제는 상수를 사용하는 것입니다. 나는 또 다른 상수 static NSString* const fullUrl = [NSString stringWithFormat:@"%@%@", kbaseUrl, @"script.php"]를 만들고 싶었지만 표현식으로 const를 만드는 것은 불법입니다. " initializer element is not constant " 오류가 발생 합니다.
JoJo

1
@Cyrille Android는 연습하기에 정말 흥미 롭습니다. iOS에서는 상상할 수없는 가능성이 있습니다! 어쨌든 답장을 보내 주셔서 감사합니다
klefevre

8
가능한 경우 #define보다 const를 선호하십시오. 컴파일 시간 검사가 향상되고 디버깅이 더 잘 작동합니다.
occulus 2013 년

2
@AnsonYao 일반적으로 저에게 그런 일이 발생하면 나는 #define에서 세미콜론을 제거하는 것을 잊었습니다. #define kBaseURL @"http://192.168.0.123/";
Gyfis

168

글쎄, 당신은 그것이 관련된 인터페이스에 로컬 선언을 원합니다. 앱 전체 상수 파일은 좋은 것이 아닙니다.

또한 다음을 extern NSString* const사용하는 것보다 단순히 기호 를 선언하는 것이 좋습니다 #define.


SomeFile.h

extern NSString* const MONAppsBaseUrl;

SomeFile.m

#import "SomeFile.h"

#ifdef DEBUG
NSString* const MONAppsBaseUrl = @"http://192.168.0.123/";
#else
NSString* const MONAppsBaseUrl = @"http://website.com/";
#endif

C ++ 호환 Extern 선언을 생략 한 것 외에도 일반적으로 Apple의 Obj-C 프레임 워크에서 사용되는 내용입니다.

상수가 하나의 파일이나 함수에만 표시되어야한다면 static NSString* const baseUrlin your *.m좋습니다.


26
수락 된 답변이 #define을 옹호하기 위해 40 표를 얻은 이유를 모르겠습니다 .const가 실제로 더 좋습니다.
occulus 2013 년

1
확실히 const NSString은 #define보다 낫습니다. 이것이 허용되는 대답이어야합니다. #define은 정의 된 값이 사용될 때마다 새 문자열을 만듭니다.
jbat100

1
@ jbat100 나는 그것이 새로운 문자열을 생성한다고 생각하지 않습니다. 컴파일러가 코드가 동일한 정적 문자열을 300,000 번 생성하는지 감지하고 한 번만 생성한다고 생각합니다. @"foo"과 같지 않습니다 [[NSString alloc] initWithCString:"foo"].
Abhi Beckert 2013

@AbhiBeckert jbat가 만들려고 한 요점 #define은 사용시 상수의 중복으로 끝날 수 있다는 것 입니다 (즉, 포인터 같음이 실패 할 수 있음)-NSString 리터럴 표현식이 실행될 때마다 임시를 생성한다는 것이 아닙니다.
justin

1
나는 #define이 나쁜 생각이라는 데 동의합니다. 여러 객체를 만들 것이라는 그가 만든 오류를 수정하고 싶었습니다. 또한 상수에 대해서도 포인터 동등성을 신뢰할 수 없습니다. NSUserDefaults 등에서로드 될 수 있습니다. 항상 isEqual :을 사용하십시오.
Abhi Beckert 2013

39

전역 상수를 정의하는 방법 :


AppConstants.h

extern NSString* const kAppBaseURL;

AppConstants.m

#import "AppConstants.h"

#ifdef DEBUG
NSString* const kAppBaseURL = @"http://192.168.0.123/";
#else
NSString* const kAppBaseURL = @"http://website.com/";
#endif

그런 다음 {$ APP} -Prefix.pch 파일에서 :

#ifdef __OBJC__
  #import <UIKit/UIKit.h>
  #import <Foundation/Foundation.h>
  #import "AppConstants.h"
#endif

문제가 발생하면 먼저 Prefix Header 옵션이 NO로 설정되어 있는지 확인하십시오.


5

다음과 같이 문자열 상수를 연결할 수도 있습니다.

  #define kBaseURL @"http://myServer.com"
  #define kFullURL kBaseURL @"/api/request"

4

이 작업을 수행하는 또 다른 방법은 훨씬 간단하며 .pch 접두사 파일과 같이 모든 파일이 아닌 필요한 파일에 포함시킬 것입니다.

#ifndef Constants_h
#define Constants_h

//Some constants
static int const ZERO = 0;
static int const ONE = 1;
static int const TWO = 2;

#endif /* Constants_h */

그런 다음 원하는 헤더 파일에이 헤더 파일을 포함합니다. 포함하려는 특정 클래스의 헤더 파일에 포함합니다.

#include "Constants.h"

내 테스트에서 정적 const 상수는 디버거 (Xcode의 lldb)에서 사용할 수 없습니다. "error: use of undeclared identifier .."
jk7

3
  1. YOURPROJECT-Prefix.pch 파일에 전역 상수를 정의합니다.
  2. #define BASEURl @"http://myWebService.appspot.com/xyz/xx"
  3. 그런 다음 BASEURL을 사용하는 프로젝트의 모든 위치 :

    NSString *LOGIN_URL= [BASEURl stringByAppendingString:@"/users/login"];

업데이트 됨 : Xcode 6에서는 프로젝트에서 생성 된 기본 .pch 파일을 찾을 수 없습니다. 따라서 Xcode 6의 PCH 파일을 사용 하여 프로젝트에 .pch 파일을 삽입하십시오.

업데이트 : SWIFT 용

  1. 새 Swift 파일 만들기 [클래스없이 비어 있음] [AppGlobalMemebers]라고 말하세요.
  2. & 즉시 멤버 선언 / 정의

    예:

    var STATUS_BAR_GREEN : UIColor  = UIColor(red: 106/255.0, green: 161/255.0, blue: 7/255.0, alpha: 1)  //
    1. Appdelegate 또는 Singleton 클래스 또는 임의의 클래스 파일에서 앱 전역 멤버를 정의하려면 클래스 정의 위에 지정된 멤버를 선언하십시오.

2

전역 선언은 흥미롭지 만 저에게있어 코딩 방식을 크게 변경 한 것은 전역 클래스 인스턴스를 갖는 것이 었습니다. 작업 방법을 이해하는 데 며칠이 걸렸기 때문에 여기에 빠르게 요약했습니다.

클래스의 글로벌 인스턴스 (필요한 경우 프로젝트 당 1 개 또는 2 개)를 사용하여 핵심 데이터 액세스 또는 일부 거래 논리를 다시 그룹화합니다.

예를 들어 모든 레스토랑 테이블을 처리하는 중앙 개체를 원하면 시작시 개체를 생성하면됩니다. 이 개체는 데이터베이스 액세스를 처리하거나 저장할 필요가없는 경우 메모리에서 처리 할 수 ​​있습니다. 중앙 집중식으로 유용한 인터페이스 만 표시합니다 ...!

그것은 큰 도움, 객체 지향적이며 모든 것을 한곳에서 얻을 수있는 좋은 방법입니다.

몇 줄의 코드 :

@interface RestaurantManager : NSObject
    +(id) sharedInstance;
    -(void)registerForTable:(NSNumber *)tableId;
@end 

및 객체 구현 :

@implementation RestaurantManager

+ (id) sharedInstance {
    static dispatch_once_t onceQueue;

    dispatch_once(&onceQueue, ^{
        sharedInstance = [[self alloc] init];
        NSLog(@"*** Shared instance initialisation ***");
    });
    return sharedInstance;
}

-(void)registerForTable:(NSNumber *)tableId {
}
@end

그것을 사용하는 것은 정말 간단합니다.

[[RestaurantManager sharedInstance] registerForTable : [NsNumber numberWithInt : 10]]


3
이 디자인 패턴의 기술적 이름은 Singleton입니다. en.wikipedia.org/wiki/Singleton_pattern
Basil Bourque

sharedmanager에 정적 데이터 (정적 클래스가 아님)를 유지하는 것은 좋은 생각이 아닙니다.
Onder OZCAN 2016

1

받아 들여지는 대답에는 두 가지 약점이 있습니다. 첫째, 다른 사람들이 #define디버깅하기 어려운 사용법을 지적 했으므로 대신 extern NSString* const kBaseUrl구조를 사용하십시오 . 둘째, 상수에 대한 단일 파일을 정의합니다. IMO, 이것은 대부분의 클래스가 해당 상수에 대한 액세스가 필요하지 않거나 모든 상수에 대한 액세스가 필요하지 않거나 모든 상수가 선언되면 파일이 비대해질 수 있기 때문에 잘못된 것입니다. 더 나은 솔루션은 3 개의 다른 레이어에서 상수를 모듈화하는 것입니다.

  1. 시스템 계층 : SystemConstants.h또는 시스템의 AppConstants.h 모든 클래스에서 액세스 할 수있는 전역 범위의 상수를 설명합니다. 관련되지 않은 다른 클래스에서 액세스해야하는 상수 만 여기에 선언하십시오.

  2. 모듈 / 하위 시스템 계층 : ModuleNameConstants.h, 모듈 / 하위 시스템 내부의 관련 클래스 집합에 대한 일반적인 상수 집합을 설명합니다.

  3. 클래스 레이어 : 상수는 클래스에 상주하며 해당 클래스에서만 사용됩니다.

1,2 만 질문과 관련이 있습니다.


0

내가 전에 사용했던 접근 방법은 파일을 만드는 것입니다 Settings.plist및에로드 NSUserDefaults하여 실행에 registerDefaults:. 그런 다음 다음을 사용하여 해당 컨텐츠에 액세스 할 수 있습니다.

// Assuming you've defined some constant kMySettingKey.
[[NSUserDefaults standardUserDefaults] objectForKey:kMySettingKey];

Android 개발을 한 적은 없지만 설명하신 문자열 리소스 파일과 유사한 것 같습니다. 유일한 단점은 전처리기를 사용하여 설정을 바꿀 수 없다는 것입니다 (예 : DEBUG모드에서). 그래도 다른 파일로로드 할 수 있다고 생각합니다.

NSUserDefaults 선적 서류 비치.


9
당신이 원하는 모든 것이 상수 일 때 그것은 약간 지나치지 않습니까? 또한 잠재적으로 수정 가능한 파일에 넣는 이유는 무엇입니까? (특히 앱이 작동하지 않는 마스터 서버의 IP만큼 중요한 경우).
Cyrille

나는이 방법은 여러 가지 혜택, 설정이 올바른 형식 (반환되는 가장 중요한 존재가 있다고 생각 NSString, NSNumber등). 물론 #define똑같은 일을하도록 래핑 할 수는 있지만 편집하기가 쉽지 않습니다. plist편집 인터페이스도 좋다. :) 암호화 키와 같은 초 비밀 항목을 거기에 넣지 말아야한다는 데 동의하지만,해서는 안되는 곳을 샅샅이 뒤지는 사용자에 대해서는별로 걱정하지 않습니다. 앱을 망가 뜨린 것은 자신의 잘못입니다. .
Chris Doble

1
네, 당신의 주장에 동의합니다. 당신이 말했듯이, 나는 #define올바른 유형을 반환하기 위해 s를 래핑 하지만, 나는 Pascal을 배운 시절부터 이와 같은 전역 상수를 별도의 상수 파일에 넣는 법을 항상 배웠기 때문에 이러한 상수 파일을 편집하는 데 익숙합니다. on an old 286 :) 그리고 모든 곳을 찌르는 사용자에 관해서는 저도 동의합니다. 그것은 그들의 잘못입니다. 정말 맛의 문제입니다.
Cyrille

@Chris Doble : 아니요, Android의 리소스 파일은 NSUserDefaults와 비슷하지 않습니다. SharedPreferences 및 Preferences는 Android에서 NSUserDefaults와 동일합니다 (NSUserDefaults보다 강력하지만). Android의 리소스는 현지화 및 기타 여러 용도로 콘텐츠에서 로직을 분리하기위한 것입니다.
mrd jul

0

숫자의 경우 다음과 같이 사용할 수 있습니다.

#define MAX_HEIGHT 12.5

0

나는 거라고 구성 객체 사용 A로부터 초기화합니다 plist. 관련없는 외부 항목으로 다른 클래스를 괴롭히는 이유는 무엇입니까?

eppz!settigns이런 이유로 솔리를 만들었습니다 . .NET Framework 에서 기본값을 통합 하기 위해 NSUserDefaults저장하는 고급이지만 간단한 방법을 참조하십시오 plist.

여기에 이미지 설명 입력


진실. 워드 프레스 어떻게 든하지 해결 ... / ... 어쨌든이 직접 링크로 이동 blog.eppz.eu/?p=926 : D
게리 Borbás
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.