Lưu trữ hằng số toàn cục trong ứng dụng iOS ở đâu?


111

Hầu hết các mô hình trong ứng dụng iOS của tôi đều truy vấn máy chủ web. Tôi muốn có một tệp cấu hình lưu trữ URL cơ sở của máy chủ. Nó sẽ trông giống như sau:

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

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

Bằng cách bình luận dòng này hay dòng khác, tôi có thể thay đổi ngay lập tức máy chủ mà mô hình của tôi trỏ đến. Câu hỏi của tôi là, cách tốt nhất để lưu trữ các hằng số toàn cục trong iOS là gì? Trong lập trình Android, chúng tôi có tệp tài nguyên chuỗi tích hợp này . Trong bất kỳ Hoạt động nào (tương đương với UIViewController ), chúng ta có thể truy xuất các hằng số chuỗi đó bằng:

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

Tôi đã tự hỏi liệu SDK iOS có một nơi tương tự để lưu trữ các hằng số hay không. Nếu không, cách tốt nhất trong Objective-C để làm như vậy là gì?

Câu trả lời:


145

Bạn cũng có thể làm một

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

trong tệp tiêu đề "hằng số", chẳng hạn constants.h. Sau đó làm

#include "constants.h"

ở đầu mỗi tệp mà bạn cần hằng số này.

Bằng cách này, bạn có thể chuyển đổi giữa các máy chủ tùy thuộc vào cờ trình biên dịch, như trong:

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

Tôi sử dụng "constants.h"cách tiếp cận, khai báo staticcác biến dựa trên #ifdef VIEW_CONSTANTS ... #endif. Vì vậy, tôi có một tệp hằng số cho toàn ứng dụng, nhưng mỗi tệp mã khác của tôi có các tập #definehợp hằng số khác nhau để bao gồm trước #include-ing tệp hằng số (dừng tất cả các cảnh báo trình biên dịch "được xác định nhưng không được sử dụng").

2
Có hai vấn đề tôi gặp phải với giải pháp này. Đầu tiên, khi tôi sử dụng #decalare, tôi gặp lỗi biên dịch nói " khai báo chỉ thị tiền xử lý không hợp lệ ". Vì vậy, tôi đã thay đổi nó thành #definethay thế. Vấn đề khác là sử dụng hằng số. Tôi muốn tạo một hằng số khác với static NSString* const fullUrl = [NSString stringWithFormat:@"%@%@", kbaseUrl, @"script.php"], nhưng rõ ràng là bất hợp pháp khi tạo các lỗi bằng một biểu thức. Tôi nhận được lỗi " phần tử khởi tạo không phải là hằng số ".
JoJo

1
@Cyrille Android thực sự rất thú vị để thực hành, có một số khả năng bạn không thể tưởng tượng trên iOS! Dù sao cũng cảm ơn bạn đã trả lời
klefevre 26/02

8
Ưu tiên const hơn #define nếu có thể - bạn kiểm tra thời gian biên dịch tốt hơn và gỡ lỗi hoạt động tốt hơn.
Occulus

2
@AnsonYao thường là khi điều đó xảy ra với tôi Tôi quên để loại bỏ dấu chấm phẩy từ #define, chẳng hạn như #define kBaseURL @"http://192.168.0.123/";
Gyfis

168

Chà, bạn muốn khai báo cục bộ cho các giao diện mà nó liên quan - tệp hằng số trên toàn ứng dụng không phải là một điều tốt.

Ngoài ra, bạn nên khai báo một extern NSString* constký hiệu đơn giản hơn là sử dụng #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

Ngoài việc bỏ sót phần khai báo Extern tương thích với C ++, đây là những gì bạn thường thấy được sử dụng trong các khung Obj-C của Apple.

Nếu hằng số chỉ cần hiển thị cho một tệp hoặc hàm, thì static NSString* const baseUrltrong của bạn *.mlà tốt.


26
Không chắc tại sao câu trả lời được chấp nhận lại có 40 phiếu ủng hộ #define - const thực sự tốt hơn.
Occulus

1
Chắc chắn const NSString tốt hơn #define, đây phải là câu trả lời được chấp nhận. #define tạo một chuỗi mới mỗi khi giá trị đã xác định được sử dụng.
jbat100

1
@ jbat100 Tôi không nghĩ rằng nó tạo ra một chuỗi mới. Tôi nghĩ rằng trình biên dịch sẽ phát hiện xem mã của bạn có tạo ra cùng một chuỗi tĩnh 300.000 lần hay không và sẽ chỉ tạo nó một lần. @"foo"không giống như [[NSString alloc] initWithCString:"foo"].
Abhi Beckert

@AbhiBeckert Tôi nghĩ rằng điểm jbat đang cố gắng thực hiện là có thể kết thúc với các bản sao của hằng số của bạn khi #defineđược sử dụng (nghĩa là bình đẳng con trỏ có thể không thành công) - không phải là biểu thức chữ NSString tạo ra một biểu thức tạm thời bất cứ khi nào được thực thi.
justin

1
Tôi đồng ý #define là một ý tưởng tồi, tôi chỉ muốn sửa lỗi anh ấy mắc phải rằng nó sẽ tạo nhiều đối tượng. Ngoài ra, không thể dựa vào bình đẳng con trỏ ngay cả đối với hằng số. Nó có thể được tải từ NSUserDefaults hoặc thứ gì đó. Luôn sử dụng isEqual:.
Abhi Beckert

39

Cách tôi xác định các hằng số toàn cục:


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

Sau đó, trong tệp {$ APP} -Prefix.pch của bạn:

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

Nếu bạn gặp bất kỳ sự cố nào, trước tiên hãy đảm bảo rằng bạn đã đặt tùy chọn Tiêu đề tiền tố biên dịch thành KHÔNG.


5

Bạn cũng có thể nối các hằng chuỗi như sau:

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

4

Tôi nghĩ rằng một cách khác để làm điều này đơn giản hơn nhiều và bạn sẽ chỉ đưa nó vào các tệp bạn cần đưa chúng vào, chứ không phải TẤT CẢ các tệp, như với tệp tiền tố .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 */

Sau đó, bạn đưa tệp tiêu đề này vào tệp tiêu đề mà bạn muốn. Bạn đưa nó vào tệp tiêu đề cho lớp cụ thể mà bạn muốn nó được đưa vào:

#include "Constants.h"

Trong thử nghiệm của tôi, các hằng số tĩnh không thể sử dụng được trong trình gỡ lỗi (lldb của Xcode). "error: use of undeclared identifier .."
jk7

3
  1. Tôi xác định hằng số toàn cầu trong tệp YOURPROJECT-Prefix.pch.
  2. #define BASEURl @"http://myWebService.appspot.com/xyz/xx"
  3. sau đó bất kỳ nơi nào trong dự án để sử dụng BASEURL:

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

Cập nhật: Trong Xcode 6, bạn sẽ không tìm thấy tệp .pch mặc định được tạo trong dự án của mình. Vì vậy, vui lòng sử dụng PCH File trong Xcode 6 để chèn tệp .pch vào dự án của bạn.

Cập nhật: Đối với SWIFT

  1. Tạo tệp Swift mới [trống không có lớp] nói [AppGlobalMemebers]
  2. & Khai báo ngay / xác định thành viên

    Thí dụ:

    var STATUS_BAR_GREEN : UIColor  = UIColor(red: 106/255.0, green: 161/255.0, blue: 7/255.0, alpha: 1)  //
    1. Nếu bạn muốn xác định thành viên toàn cầu của ứng dụng trong bất kỳ tệp lớp nào, hãy nói lớp Appdelegate hoặc Singleton hoặc bất kỳ, hãy khai báo thành viên đã cho ở trên định nghĩa lớp

2

Các khai báo toàn cục rất thú vị nhưng đối với tôi, điều đã làm thay đổi sâu sắc cách viết mã của tôi là có các phiên bản chung của các lớp. Tôi đã mất vài ngày để thực sự hiểu cách làm việc với nó nên tôi đã nhanh chóng tóm tắt nó ở đây

Tôi sử dụng các phiên bản toàn cầu của các lớp (1 hoặc 2 cho mỗi dự án, nếu cần), để tập hợp lại quyền truy cập dữ liệu cốt lõi hoặc một số lôgic giao dịch.

Ví dụ: nếu bạn muốn có một đối tượng trung tâm xử lý tất cả các bảng nhà hàng mà bạn tạo đối tượng bạn khi khởi động và đó là nó. Đối tượng này có thể xử lý các truy cập cơ sở dữ liệu HOẶC xử lý nó trong bộ nhớ nếu bạn không cần lưu nó. Nó tập trung, bạn chỉ hiển thị các giao diện hữu ích ...!

Đó là một trợ giúp tuyệt vời, hướng đối tượng và là một cách tốt để có được tất cả nội dung của bạn ở cùng một nơi

Một vài dòng mã:

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

và thực hiện đối tượng:

@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

để sử dụng nó thực sự đơn giản:

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


3
Tên kỹ thuật của mẫu thiết kế này là Singleton. vi.wikipedia.org/wiki/Singleton_pattern
Basil Bourque,

Giữ dữ liệu tĩnh (Không phải Lớp tĩnh) trong sharedmanager không phải là một ý kiến ​​hay.
Onder OZCAN

1

Câu trả lời được chấp nhận có 2 điểm yếu. Đầu tiên, như những người khác đã chỉ ra là cách sử dụng #definekhó gỡ lỗi hơn, hãy sử dụng extern NSString* const kBaseUrlcấu trúc thay thế . Thứ hai, nó định nghĩa một tệp duy nhất cho các hằng số. IMO, điều này là sai vì hầu hết các lớp không cần quyền truy cập vào các hằng số đó hoặc để truy cập tất cả chúng cộng với tệp có thể trở nên cồng kềnh nếu tất cả các hằng số được khai báo ở đó. Một giải pháp tốt hơn sẽ là modularize hằng số ở 3 lớp khác nhau:

  1. Lớp hệ thống: SystemConstants.hhoặc lớp AppConstants.h mô tả các hằng số ở phạm vi toàn cục, có thể được truy cập bởi bất kỳ lớp nào trong hệ thống. Khai báo ở đây chỉ những hằng số phải được truy cập từ các lớp khác nhau không liên quan.

  2. Lớp mô-đun / hệ thống con:, ModuleNameConstants.hmô tả một tập hợp các hằng số tiêu biểu cho một tập hợp các lớp liên quan, bên trong một mô-đun / hệ thống con.

  3. Lớp lớp: Các hằng số nằm trong lớp và chỉ được sử dụng bởi nó.

Chỉ 1,2 là liên quan đến câu hỏi.


0

Một cách tiếp cận mà tôi đã sử dụng trước đây là tạo một tệp Settings.plistvà tải nó vào NSUserDefaultskhi khởi chạy bằng cách sử dụng registerDefaults:. Sau đó, bạn có thể truy cập nội dung của nó bằng những thứ sau:

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

Mặc dù tôi chưa thực hiện bất kỳ phát triển Android nào, nhưng có vẻ như điều này tương tự như tệp tài nguyên chuỗi mà bạn đã mô tả. Nhược điểm duy nhất là bạn không thể sử dụng bộ tiền xử lý để hoán đổi giữa các cài đặt (ví dụ: trong DEBUGchế độ). Tuy nhiên, tôi cho rằng bạn có thể tải trong một tệp khác.

NSUserDefaults tài liệu.


9
Đó không phải là một chút quá mức cần thiết khi tất cả những gì bạn muốn là một hằng số? Và ngoài ra, tại sao lại đặt nó vào một tệp có khả năng sửa đổi? (Đặc biệt khi đó là thứ quan trọng như IP của máy chủ chính của bạn mà ứng dụng của bạn không hoạt động mà không có).
Cyrille

Tôi cảm thấy rằng phương pháp này có nhiều lợi ích, những con người quan trọng nhất mà các thiết lập của bạn được trả về trong định dạng đúng ( NSString, NSNumber, vv). Chắc chắn, bạn có thể quấn các chữ cái của mình #defineđể làm điều tương tự, nhưng sau đó chúng không dễ chỉnh sửa. Các plistgiao diện chỉnh sửa là tốt đẹp, quá. :) Mặc dù tôi đồng ý rằng bạn không nên đặt những thứ siêu bí mật như khóa mã hóa vào đó, nhưng tôi không quá lo lắng về việc người dùng đang tìm kiếm ở những nơi mà họ không nên ở — nếu họ phá vỡ ứng dụng, đó là lỗi của chính họ .
Chris Doble,

1
Chắc chắn, tôi đồng ý với lập luận của bạn. Như bạn nói, tôi quấn các chữ cái của mình #defineđể trả về đúng kiểu, nhưng tôi đã quen với việc chỉnh sửa các tệp hằng số như vậy, vì tôi đã luôn học cách đặt các hằng số toàn cục như thế này trong một tệp hằng số riêng biệt, từ những ngày tôi học Pascal. trên một 286 cũ :) Và đối với người dùng chọc phá khắp nơi, tôi cũng đồng ý, đó là lỗi của họ. Nó chỉ là một vấn đề của hương vị, thực sự.
Cyrille

@Chris Doble: Không, các tệp tài nguyên trong Android không giống với NSUserDefaults. SharedPreferences và Preferences là Android tương đương với NSUserDefaults (mặc dù mạnh hơn NSUserDefaults). Các tài nguyên trong Android nhằm mục đích tách logic khỏi nội dung, như để bản địa hóa và cho nhiều mục đích sử dụng khác.
mrd

0

Đối với một số bạn có thể sử dụng nó như

#define MAX_HEIGHT 12.5

0

Tôi sẽ sử dụng một đối tượng cấu hình khởi tạo từ mộtplist . Tại sao phải làm phiền các lớp khác với những thứ bên ngoài không liên quan?

Tôi tạo ra eppz!settignssoley vì lý do này. Xem bài viết Cách nâng cao nhưng đơn giản để lưu vào NSUserDefaults để kết hợp các giá trị mặc định từ a plist.

nhập mô tả hình ảnh ở đây


Thật. Wordpress bằng cách nào đó không giải quyết được ...: / ... đi với liên kết này trực tiếp nào blog.eppz.eu/?p=926 : D
Geri Borbás
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.