จะจัดเก็บค่าคงที่ทั่วโลกในแอปพลิเคชัน 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.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ฉันพบข้อผิดพลาดในการคอมไพล์ว่า " ไม่ถูกต้องคำสั่งก่อนการประมวลผลคำสั่งประกาศ " เลยเปลี่ยนมา#defineใช้แทน ปัญหาอื่น ๆ คือการใช้ค่าคงที่ ฉันต้องการสร้างค่าคงที่อื่นด้วยstatic NSString* const fullUrl = [NSString stringWithFormat:@"%@%@", kbaseUrl, @"script.php"]แต่เห็นได้ชัดว่าการสร้าง const ด้วยนิพจน์นั้นผิดกฎหมาย ฉันได้รับข้อผิดพลาด " องค์ประกอบเริ่มต้นไม่คงที่ "
JoJo

1
@Cyrille Android น่าสนใจในการฝึกฝนมีความเป็นไปได้บางอย่างที่คุณไม่สามารถจินตนาการได้บน iOS! ขอบคุณสำหรับการตอบกลับ
klefevre

8
ชอบ const มากกว่า #define หากเป็นไปได้คุณจะได้รับการตรวจสอบเวลาคอมไพล์ที่ดีขึ้นและการดีบักจะทำงานได้ดีขึ้น
เกิดขึ้น

2
@AnsonYao โดยปกติเมื่อสิ่งนั้นเกิดขึ้นกับฉันฉันลืมลบอัฒภาคออกจาก #define เช่น #define kBaseURL @"http://192.168.0.123/";
Gyfis

168

คุณต้องการให้การประกาศภายในกับอินเทอร์เฟซที่เกี่ยวข้อง - ไฟล์ค่าคงที่ทั่วทั้งแอปไม่ใช่สิ่งที่ดี

เช่นกันควรเพียงแค่ประกาศextern NSString* constสัญลักษณ์แทนที่จะใช้ a #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

นอกเหนือจากการละเว้นการประกาศ Extern ที่เข้ากันได้กับ C ++ แล้วนี่คือสิ่งที่คุณจะเห็นโดยทั่วไปใช้ในเฟรมเวิร์ก Obj-C ของ Apple

หากค่าคงที่ต้องมองเห็นได้ในไฟล์หรือฟังก์ชันเดียวแสดงว่าstatic NSString* const baseUrlในของคุณ*.mดี


26
ไม่แน่ใจว่าทำไมคำตอบที่ได้รับการยอมรับจึงมีคะแนนโหวต 40 คะแนนสำหรับการสนับสนุน #define - const ดีกว่าแน่นอน
เกิด

1
แน่นอน const NSString ดีกว่า #define นี่ควรเป็นคำตอบที่ยอมรับได้ #define สร้างสตริงใหม่ทุกครั้งที่ใช้ค่าที่กำหนด
jbat100

1
@ jbat100 ฉันไม่คิดว่ามันจะสร้างสตริงใหม่ ฉันคิดว่าคอมไพลเลอร์ตรวจพบว่าโค้ดของคุณสร้างสตริงแบบคงที่ 300,000 ครั้งและจะสร้างเพียงครั้งเดียว @"foo"ไม่เหมือนกับ[[NSString alloc] initWithCString:"foo"].
Abhi Beckert

@AbhiBeckert ฉันคิดว่าจุดที่ jbat พยายามทำคือมันเป็นไปได้ที่จะลงเอยด้วยค่าคงที่ซ้ำกันของคุณเมื่อ#defineถูกใช้ (เช่นความเท่าเทียมกันของตัวชี้อาจล้มเหลว) - ไม่ใช่ว่านิพจน์ตามตัวอักษร NSString จะสร้างชั่วคราวทุกครั้งที่ดำเนินการ
justin

1
ฉันยอมรับ #define เป็นความคิดที่ไม่ดีฉันแค่อยากจะแก้ไขข้อผิดพลาดที่เขาทำว่าจะสร้างวัตถุหลายชิ้น นอกจากนี้ความเท่าเทียมกันของตัวชี้ไม่สามารถพึ่งพาได้แม้กระทั่งค่าคงที่ อาจโหลดจาก NSUserDefaults หรือบางอย่าง ใช้ isEqual เสมอ:.
Abhi Beckert

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

หากคุณประสบปัญหาใด ๆ ขั้นแรกตรวจสอบให้แน่ใจว่าคุณได้ตั้งค่าตัวเลือกส่วนหัวของคำนำหน้าคำนำหน้าไว้เป็น 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"

ในการทดสอบของฉันค่าคงที่คงที่ใช้ไม่ได้ในดีบักเกอร์ (lldb ของ Xcode) "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 เริ่มต้นที่สร้างขึ้นในโครงการของคุณ ดังนั้นโปรดใช้ไฟล์ PCH ใน Xcode 6เพื่อแทรกไฟล์. 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

1

คำตอบที่ยอมรับมี 2 จุดอ่อน ประการแรกเนื่องจากคนอื่น ๆ ชี้ให้เห็นคือการใช้งาน#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ฯลฯ ) แน่นอนว่าคุณสามารถรวม#defines ของคุณเพื่อทำสิ่งเดียวกัน แต่ก็ไม่ง่ายที่จะแก้ไข plistอินเตอร์เฟซการแก้ไขเป็นสิ่งที่ดีมากเกินไป :) แม้ว่าฉันจะยอมรับว่าคุณไม่ควรใส่สิ่งที่เป็นความลับสุดยอดเช่นคีย์การเข้ารหัสไว้ในนั้น แต่ฉันก็ไม่ได้กังวลเกี่ยวกับผู้ใช้ที่ไปโผล่ในสถานที่ที่พวกเขาไม่ควรอยู่มากนักหากพวกเขาทำลายแอปก็เป็นความผิดของพวกเขาเอง .
Chris Doble

1
แน่นอนฉันเห็นด้วยกับข้อโต้แย้งของคุณ อย่างที่คุณพูดฉันห่อ#defines เพื่อส่งคืนประเภทที่ถูกต้อง แต่ฉันคุ้นเคยกับการแก้ไขไฟล์ค่าคงที่ดังกล่าวเนื่องจากฉันได้เรียนรู้ที่จะใส่ค่าคงที่ส่วนกลางเช่นนี้ในไฟล์ค่าคงที่แยกต่างหากตั้งแต่สมัยที่ฉันเรียนรู้ภาษาปาสคาล ใน 286 เก่า :) และสำหรับผู้ใช้ที่โผล่ไปทั่วทุกที่ฉันก็เห็นด้วยเช่นกันมันเป็นความผิดของพวกเขา มันเป็นเพียงเรื่องของรสนิยมจริงๆ
Cyrille

@Chris Doble: ไม่ไฟล์ทรัพยากรใน Android ไม่เหมือนกับ NSUserDefaults SharedPreferences และ Preferences เป็น Android ที่เทียบเท่ากับ NSUserDefaults (แม้ว่าจะมีประสิทธิภาพมากกว่า NSUserDefaults) ทรัพยากรใน Android มีจุดประสงค์เพื่อแยกตรรกะออกจากเนื้อหาสำหรับการแปลเป็นภาษาท้องถิ่นและสำหรับการใช้งานอื่น ๆ อีกมากมาย
mrd


0

ฉันจะใช้ออบเจ็กต์คอนฟิกูเรชันที่เริ่มต้นจากไฟล์plist. ทำไมต้องรบกวนชั้นเรียนอื่นด้วยสิ่งภายนอกที่ไม่เกี่ยวข้อง?

ฉันสร้างeppz!settignssoley ด้วยเหตุนี้ ดูบทความขั้นสูง แต่วิธีง่ายๆในการบันทึกลงใน NSUserDefaultsสำหรับการรวมค่าเริ่มต้นจากไฟล์plist.

ใส่คำอธิบายภาพที่นี่


จริง Wordpress ไม่สามารถแก้ไขได้ ... : / ... ไปกับลิงก์โดยตรงนี้ต่อไปblog.eppz.eu/?p=926 : D
Geri Borbás
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.