Di mana menyimpan konstanta global dalam aplikasi iOS?

111

Sebagian besar model di aplikasi iOS saya meminta server web. Saya ingin memiliki file konfigurasi yang menyimpan URL dasar server. Ini akan terlihat seperti ini:

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

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

Dengan mengomentari satu baris atau lainnya, saya dapat langsung mengubah server mana yang ditunjuk oleh model saya. Pertanyaan saya adalah, apa praktik terbaik untuk menyimpan konstanta global di iOS? Dalam pemrograman Android, kami memiliki file sumber daya string bawaan ini . Dalam Aktivitas apa pun (setara dengan UIViewController ), kita bisa mengambil konstanta string tersebut dengan:

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

Saya ingin tahu apakah iOS SDK memiliki tempat analog untuk menyimpan konstanta. Jika tidak, apa praktik terbaik di Objective-C untuk melakukannya?

JoJo
sumber

Jawaban:

145

Anda juga bisa melakukan

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

dalam file header "konstanta", misalnya constants.h. Lalu lakukan

#include "constants.h"

di bagian atas setiap file yang memerlukan konstanta ini.

Dengan cara ini, Anda dapat beralih antar server bergantung pada tanda compiler, seperti di:

#ifdef DEBUG
    #define kBaseURL @"http://192.168.0.123/"
#else
    #define kBaseURL @"http://myproductionserver.com/"
#endif
Cyrille
sumber
Saya menggunakan "constants.h"pendekatan, mendeklarasikan staticvariabel berdasarkan #ifdef VIEW_CONSTANTS ... #endif. Jadi saya memiliki satu file konstanta di seluruh aplikasi, tetapi masing-masing file kode saya yang lain #definememiliki kumpulan konstanta yang berbeda untuk disertakan sebelum #include-ing file konstanta (menghentikan semua peringatan kompilator yang "ditentukan tetapi tidak digunakan").
2
Ada dua masalah yang saya hadapi dengan solusi ini. Pertama, ketika saya menggunakan #decalare, saya mendapat kesalahan kompilasi yang mengatakan " deklarasi direktif preprocessing tidak valid ". Jadi saya mengubahnya menjadi #definesebagai gantinya. Masalah lainnya adalah menggunakan konstanta. Saya ingin membuat konstanta lain dengan static NSString* const fullUrl = [NSString stringWithFormat:@"%@%@", kbaseUrl, @"script.php"], tetapi tampaknya ilegal untuk membuat konstanta dengan ekspresi. Saya mendapatkan kesalahan " elemen penginisialisasi tidak konstan ".
JoJo
1
@Cyrille Android sangat menarik untuk dipraktikkan, ada beberapa kemungkinan yang tidak dapat Anda bayangkan di iOS! Terima kasih atas balasannya
klefevre
8
Lebih suka const daripada #define jika memungkinkan - Anda mendapatkan pemeriksaan waktu kompilasi yang lebih baik, dan proses debug berfungsi lebih baik.
occulus
2
@AnsonYao biasanya ketika itu terjadi pada saya, saya lupa menghapus titik koma dari #define, seperti #define kBaseURL @"http://192.168.0.123/";
Gyfis
168

Nah, Anda ingin deklarasi lokal ke antarmuka yang terkait - file konstanta di seluruh aplikasi bukanlah hal yang baik.

Selain itu, lebih baik untuk mendeklarasikan extern NSString* constsimbol, daripada menggunakan #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

Terlepas dari penghilangan deklarasi Extern yang kompatibel dengan C ++, inilah yang biasanya Anda lihat digunakan dalam kerangka kerja Obj-C Apple.

Jika konstanta perlu terlihat hanya untuk satu file atau fungsi, maka static NSString* const baseUrlAnda *.msudah baik.

justin
sumber
26
Tidak yakin mengapa jawaban yang diterima memiliki 40 suara untuk mendukung #define - const memang lebih baik.
occulus
1
Jelas const NSString lebih baik daripada #define, ini harus menjadi jawaban yang diterima. #define membuat string baru setiap kali nilai yang ditentukan digunakan.
jbat100
1
@ jbat100 Saya rasa ini tidak membuat string baru. Saya pikir kompilator mendeteksi jika kode Anda membuat string statis yang sama 300.000 kali dan hanya akan membuatnya sekali. @"foo"tidak sama dengan [[NSString alloc] initWithCString:"foo"].
Abhi Beckert
@ AbhiBeckert saya pikir titik jbat mencoba untuk membuat adalah bahwa mungkin untuk berakhir dengan duplikat dari konstanta Anda ketika #definedigunakan (yaitu persamaan pointer bisa gagal) - bukan bahwa ekspresi literal NSString menghasilkan sementara setiap kali dijalankan.
justin
1
Saya setuju #define adalah ide yang buruk, saya hanya ingin memperbaiki kesalahan yang dia buat yang akan membuat banyak objek. Juga, persamaan pointer tidak dapat diandalkan bahkan untuk konstanta. Mungkin dimuat dari NSUserDefaults atau semacamnya. Selalu gunakan isEqual :.
Abhi Beckert
39

Cara saya mendefinisikan konstanta global:


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

Kemudian di file {$ APP} -Prefix.pch Anda:

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

Jika Anda mengalami masalah apa pun, pertama-tama pastikan bahwa Anda memiliki opsi Precompile Prefix Header yang disetel ke NO.

Piotr Tomasik
sumber
5

Anda juga bisa menggabungkan konstanta string seperti ini:

  #define kBaseURL @"http://myServer.com"
  #define kFullURL kBaseURL @"/api/request"
Martin Reichl
sumber
4

Saya pikir cara lain untuk melakukan ini jauh lebih sederhana dan Anda hanya akan memasukkannya ke dalam file yang Anda perlukan, bukan SEMUA file, seperti dengan file awalan .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 */

Setelah itu Anda memasukkan file header ini ke dalam file header yang Anda inginkan. Anda memasukkannya ke dalam file header untuk kelas tertentu yang Anda inginkan untuk disertakan:

#include "Constants.h"
Vladimir Despotovic
sumber
Dalam pengujian saya, konstanta statis tidak dapat digunakan di debugger (Xcode's lldb). "error: use of undeclared identifier .."
jk7
3
  1. Saya mendefinisikan konstanta global dalam file YOURPROJECT-Prefix.pch.
  2. #define BASEURl @"http://myWebService.appspot.com/xyz/xx"
  3. lalu di mana saja dalam proyek untuk menggunakan BASEURL:

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

Diperbarui: Di ​​Xcode 6 Anda tidak akan menemukan file .pch default yang dibuat dalam proyek Anda. Jadi silakan gunakan File PCH di Xcode 6 untuk memasukkan file .pch dalam proyek Anda.

Pembaruan: Untuk SWIFT

  1. Buat file Swift baru [kosong tanpa kelas] katakan [AppGlobalMemebers]
  2. & Segera deklarasikan / definisikan anggota

    Contoh:

    var STATUS_BAR_GREEN : UIColor  = UIColor(red: 106/255.0, green: 161/255.0, blue: 7/255.0, alpha: 1)  //
    1. Jika Anda ingin mendefinisikan anggota global aplikasi dalam file kelas apa pun, katakan Appdelegate atau Singleton class atau apa pun, nyatakan anggota yang diberikan di atas definisi kelas
Yogesh Lolusare
sumber
2

Deklarasi global menarik, tetapi bagi saya, yang sangat mengubah cara saya membuat kode adalah memiliki contoh kelas global. Saya butuh beberapa hari untuk benar-benar memahami cara bekerja dengannya, jadi saya segera meringkasnya di sini

Saya menggunakan contoh kelas global (1 atau 2 per proyek, jika diperlukan), untuk mengelompokkan kembali akses data inti, atau beberapa logika perdagangan.

Misalnya jika Anda ingin memiliki objek pusat yang menangani semua tabel restoran yang Anda buat, objek Anda saat startup dan hanya itu. Objek ini dapat menangani akses database ATAU menanganinya dalam memori jika Anda tidak perlu menyimpannya. Ini terpusat, Anda hanya menampilkan antarmuka yang berguna ...!

Ini sangat membantu, berorientasi pada objek, dan cara yang baik untuk mendapatkan semua barang Anda di tempat yang sama

Beberapa baris kode:

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

dan implementasi objek:

@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

untuk menggunakannya sangat sederhana:

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

Gregoire Mulliez
sumber
3
Nama teknis untuk pola desain ini adalah Singleton. en.wikipedia.org/wiki/Singleton_pattern
Basil Bourque
Menyimpan data statis (Bukan Kelas Statis) di sharedmanager bukanlah ide yang baik.
Onder OZCAN
1

Jawaban yang diterima memiliki 2 kelemahan. Pertama, seperti yang ditunjukkan orang lain adalah penggunaan #defineyang lebih sulit untuk di-debug, gunakan extern NSString* const kBaseUrlstruktur sebagai gantinya . Kedua, ini mendefinisikan satu file untuk konstanta. IMO, ini salah karena sebagian besar kelas tidak memerlukan akses ke konstanta tersebut atau untuk mengakses semuanya ditambah file bisa menjadi membengkak jika semua konstanta dideklarasikan di sana. Solusi yang lebih baik adalah dengan memodulasi konstanta pada 3 lapisan berbeda:

  1. Lapisan sistem: SystemConstants.hatau AppConstants.h yang mendeskripsikan konstanta dalam lingkup global, yang dapat diakses oleh semua kelas dalam sistem. Deklarasikan di sini hanya konstanta yang harus diakses dari kelas berbeda yang tidak terkait.

  2. Lapisan modul / sub-sistem:, ModuleNameConstants.hmenjelaskan sekumpulan konstanta yang khas untuk sekumpulan kelas terkait, di dalam modul / sub-sistem.

  3. Lapisan kelas: Konstanta berada di dalam kelas dan hanya digunakan olehnya.

Hanya 1,2 yang terkait dengan pertanyaan tersebut.

Stefan
sumber
0

Pendekatan yang saya gunakan sebelumnya adalah membuat file Settings.plistdan memuatnya ke dalam NSUserDefaultssaat peluncuran menggunakan registerDefaults:. Anda kemudian dapat mengakses isinya dengan yang berikut:

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

Meskipun saya belum melakukan pengembangan Android apa pun, sepertinya ini analog dengan file sumber daya string yang Anda jelaskan. Satu-satunya downside adalah Anda tidak dapat menggunakan preprocessor untuk berpindah antar pengaturan (misalnya dalam DEBUGmode). Saya kira Anda dapat memuat file yang berbeda.

NSUserDefaults dokumentasi.

Chris Doble
sumber
9
Bukankah itu berlebihan jika yang Anda inginkan adalah sebuah konstanta? Dan juga, mengapa memasukkannya ke dalam file yang berpotensi dapat dimodifikasi? (Terutama bila itu adalah sesuatu yang sama pentingnya dengan IP server master Anda, yang tanpanya aplikasi Anda tidak berfungsi).
Cyrille
Saya merasa bahwa pendekatan ini memiliki beberapa keuntungan, yang paling signifikan bahwa pengaturan Anda kembali dalam format yang benar ( NSString, NSNumber, dll). Tentu, Anda dapat membungkus #defines Anda untuk melakukan hal yang sama, tetapi itu tidak mudah untuk diedit. The plistantarmuka mengedit baik, juga. :) Meskipun saya setuju bahwa Anda tidak boleh meletakkan hal-hal super rahasia seperti kunci enkripsi di sana, saya tidak terlalu khawatir tentang pengguna yang mencari-cari di tempat-tempat yang seharusnya tidak mereka kunjungi — jika mereka merusak aplikasi, itu adalah kesalahan mereka sendiri .
Chris Doble
1
Tentu, saya setuju dengan argumen Anda. Seperti yang Anda katakan, saya membungkus #defines saya untuk mengembalikan tipe yang benar, tetapi saya terbiasa mengedit file konstanta seperti itu, karena saya selalu belajar untuk meletakkan konstanta global seperti ini dalam file konstanta terpisah, sejak saya mempelajari Pascal di 286 lama :) Dan untuk pengguna yang melihat-lihat di mana-mana, saya setuju juga, itu salah mereka. Ini hanya soal selera, kok.
Cyrille
@Chris Doble: Tidak, file sumber daya di Android tidak sama dengan NSUserDefaults. SharedPreferences dan Preferences adalah Android yang setara dengan NSUserDefaults (meskipun lebih kuat daripada NSUserDefaults). Sumber daya di Android dimaksudkan untuk memisahkan logika dari konten, untuk pelokalan, dan untuk banyak kegunaan lainnya.
mrd
0

Untuk nomor Anda bisa menggunakannya seperti

#define MAX_HEIGHT 12.5
Gihan
sumber