Các biến cấp độ tĩnh của Objective-C


143

Tôi có một lớp Phim, mỗi phim lưu một ID duy nhất. Trong C #, Java, v.v. Điều này có thể được thực hiện trong Objective-C không? Tôi đã rất khó để tìm ra câu trả lời cho điều này.

Câu trả lời:


158

Mô tả sự cố :

  1. Bạn muốn ClassA của bạn có một biến lớp ClassB.
  2. Bạn đang sử dụng Objective-C làm ngôn ngữ lập trình.
  3. Objective-C không hỗ trợ các biến lớp như C ++.

Một thay thế :

Mô phỏng một hành vi biến lớp bằng các tính năng Objective-C

  1. Khai báo / Xác định một biến tĩnh trong classA.m để nó chỉ có thể truy cập được đối với các phương thức classA (và mọi thứ bạn đặt trong classA.m).

  2. Ghi đè phương thức khởi tạo NSObject để khởi tạo chỉ một lần biến tĩnh với một thể hiện của ClassB.

  3. Bạn sẽ tự hỏi, tại sao tôi nên ghi đè lên phương thức khởi tạo NSObject. Tài liệu của Apple về phương thức này có câu trả lời: "Thời gian chạy gửi khởi tạo cho mỗi lớp trong một chương trình chính xác một lần ngay trước lớp hoặc bất kỳ lớp nào kế thừa từ nó, được gửi tin nhắn đầu tiên từ bên trong chương trình. có thể không bao giờ được gọi nếu lớp không được sử dụng.) ".

  4. Vui lòng sử dụng biến tĩnh trong bất kỳ phương thức lớp / thể hiện ClassA nào.

Mẫu mã :

tập tin: classA.m

static ClassB *classVariableName = nil;

@implementation ClassA

...
 
+(void) initialize
{
    if (! classVariableName)
        classVariableName = [[ClassB alloc] init];
}

+(void) classMethodName
{
    [classVariableName doSomething]; 
}

-(void) instanceMethodName
{
    [classVariableName doSomething]; 
}

...

@end

Tài liệu tham khảo :

  1. Các biến lớp giải thích so sánh các cách tiếp cận Objective-C và C ++

3
Bạn có thể có một biến tĩnh của Type ClassA trong classA.m không?

6
đây có thể là một câu hỏi ngớ ngẩn nhưng những gì về việc phát hành bộ nhớ đã nói? không quan trọng bởi vì nó phải tồn tại bao lâu thì ứng dụng đang chạy?
samiq

1
@samiq, kiểm tra Mục tiêu-C: Tại sao giữ lại một biến tĩnh? . Con trỏ đến đối tượng không thể bị xóa, nhưng bản thân đối tượng có thể. Bạn có thể không muốn phát hành nó bởi vì rất có thể bạn muốn có nó miễn là ứng dụng đang chạy, nhưng bạn sẽ tiết kiệm bộ nhớ nếu bạn phát hành nó, vì vậy nếu bạn biết bạn không cần nó nữa, thì bạn nên phát hành nó.
ma11hew28

5
Nếu khởi tạo () được đảm bảo chỉ được gọi một lần, tại sao bạn cần "if (! ClassVariableName)" có điều kiện?
jb

23
@jamie, initializeđược gọi một lần cho mỗi lớp (siêu lớp trước lớp con), nhưng nếu một lớp con không ghi đè initialize, lớp cha initializesẽ được gọi lại. Do đó, cần có người bảo vệ nếu bạn không muốn mã đó thực thi hai lần. Xem Khởi tạo một đối tượng lớp trong các tài liệu Objective-C của Apple.
big_m

31

Kể từ Xcode 8, bạn có thể định nghĩa các thuộc tính lớp trong Obj-C. Điều này đã được thêm vào để tương tác với các thuộc tính tĩnh của Swift.

Objective-C hiện hỗ trợ các thuộc tính lớp, tương tác với các thuộc tính loại Swift. Chúng được khai báo là: @property (class) NSString * someStringProperty;. Chúng không bao giờ được tổng hợp. (23891898)

Đây là một ví dụ

@interface YourClass : NSObject

@property (class, nonatomic, assign) NSInteger currentId;

@end

@implementation YourClass

static NSInteger _currentId = 0;

+ (NSInteger)currentId {
    return _currentId;
}

+ (void)setCurrentId:(NSInteger)newValue {
    _currentId = newValue;
}

@end

Sau đó, bạn có thể truy cập nó như thế này:

YourClass.currentId = 1;
val = YourClass.currentId;

Dưới đây là một bài giải thích rất thú vị mà tôi đã sử dụng làm tài liệu tham khảo để chỉnh sửa câu trả lời cũ này.


2011 Trả lời: (không sử dụng cái này, nó rất tệ)

Nếu bạn thực sự không muốn khai báo một biến toàn cục, thì có một tùy chọn khác, có thể không chính thống lắm :-), nhưng hoạt động ... Bạn có thể khai báo một phương thức "get & set" như thế này, với một biến tĩnh bên trong:

+ (NSString*)testHolder:(NSString*)_test {
    static NSString *test;

    if(_test != nil) {
        if(test != nil)
            [test release];
        test = [_test retain];
    }

    // if(test == nil)
    //     test = @"Initialize the var here if you need to";

    return test;
}

Vì vậy, nếu bạn cần lấy giá trị, chỉ cần gọi:

NSString *testVal = [MyClass testHolder:nil]

Và sau đó, khi bạn muốn đặt nó:

[MyClass testHolder:testVal]

Trong trường hợp bạn muốn có thể đặt pseudo-static-var này thành nil, bạn có thể khai báo testHoldernhư sau:

+ (NSString*)testHolderSet:(BOOL)shouldSet newValue:(NSString*)_test {
    static NSString *test;

    if(shouldSet) {
        if(test != nil)
            [test release];
        test = [_test retain];
    }

    return test;
}

Và hai phương pháp tiện dụng:

+ (NSString*)test {
    return [MyClass testHolderSet:NO newValue:nil];
}

+ (void)setTest:(NSString*)_test {
    [MyClass testHolderSet:YES newValue:_test];
}

Hy vọng nó giúp! Chúc may mắn.


Thật tuyệt, nhưng nó không thực sự là một biến toàn cục vì nó không thể được truy cập từ các .mtệp khác và tôi nghĩ rằng nó là "toàn cầu" trong Class.mtệp.
ma11hew28

29

Trên tệp .m của bạn, bạn có thể khai báo một biến là tĩnh:

static ClassName *variableName = nil;

Sau đó, bạn có thể khởi tạo nó trên +(void)initializephương pháp của bạn .

Xin lưu ý rằng đây là biến tĩnh C đơn giản và không tĩnh theo nghĩa Java hoặc C # xem xét nó, nhưng sẽ mang lại kết quả tương tự.


16

Trong tệp .m của bạn, khai báo một biến toàn cục của tệp:

static int currentID = 1;

sau đó trong thói quen init của bạn, hãy xem lại rằng:

- (id) init
{
    self = [super init];
    if (self != nil) {
        _myID = currentID++; // not thread safe
    }
    return self;
}

hoặc nếu nó cần thay đổi vào một thời điểm khác (ví dụ: trong phương thức openConnection của bạn), sau đó tăng nó ở đó. Hãy nhớ rằng nó không phải là luồng an toàn, vì bạn sẽ cần phải thực hiện đồng bộ hóa (hoặc tốt hơn nữa, sử dụng một bổ sung nguyên tử) nếu có thể có bất kỳ vấn đề luồng nào.


11

Như pgb đã nói, không có "biến lớp", chỉ "biến thể hiện". Cách mục tiêu-c thực hiện các biến lớp là một biến toàn cục tĩnh bên trong tệp .m của lớp. "Tĩnh" đảm bảo rằng biến không thể được sử dụng bên ngoài tệp đó (nghĩa là nó không thể ở bên ngoài).


3

Đây sẽ là một lựa chọn:

+(int)getId{
    static int id;
    //Do anything you need to update the ID here
    return id;
}

Lưu ý rằng phương thức này sẽ là phương thức duy nhất để truy cập id, vì vậy bạn sẽ phải cập nhật nó bằng cách nào đó trong mã này.


2

(Nói đúng ra không phải là một câu trả lời cho câu hỏi, nhưng theo kinh nghiệm của tôi có thể hữu ích khi tìm kiếm các biến lớp)

Một phương thức lớp thường có thể đóng nhiều vai trò mà một biến lớp sẽ làm trong các ngôn ngữ khác (ví dụ: thay đổi cấu hình trong các thử nghiệm):

@interface MyCls: NSObject
+ (NSString*)theNameThing;
- (void)doTheThing;
@end
@implementation
+ (NSString*)theNameThing { return @"Something general"; }
- (void)doTheThing {
  [SomeResource changeSomething:[self.class theNameThing]];
}
@end

@interface MySpecialCase: MyCls
@end
@implementation
+ (NSString*)theNameThing { return @"Something specific"; }
@end

Bây giờ, một đối tượng của các MyClscuộc gọi lớp Resource:changeSomething:với chuỗi @"Something general"khi có cuộc gọi đến doTheThing:, nhưng một đối tượng xuất phát từ MySpecialCasechuỗi @"Something specific".


0

bạn có thể đổi tên lớp thành classA.mm và thêm các tính năng C ++ trong đó.


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.