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.