วัตถุประสงค์ระดับ C ตัวแปรระดับคงที่


143

ฉันมีคลาสฟิล์มซึ่งแต่ละอันเก็บ ID ที่ไม่ซ้ำกัน ใน C #, Java เป็นต้นฉันสามารถกำหนด static int currentID และทุกครั้งที่ฉันตั้ง ID ฉันสามารถเพิ่ม currentID และการเปลี่ยนแปลงที่เกิดขึ้นในระดับชั้นไม่ใช่ระดับวัตถุ สามารถทำได้ใน Objective-C หรือไม่ ฉันพบว่ามันยากมากที่จะหาคำตอบสำหรับสิ่งนี้

คำตอบ:


158

คำอธิบายปัญหา :

  1. คุณต้องการ ClassA ของคุณมีตัวแปรคลาส ClassB
  2. คุณใช้ Objective-C เป็นภาษาการเขียนโปรแกรม
  3. Objective-C ไม่สนับสนุนตัวแปรคลาสเช่น C ++

ทางเลือกหนึ่ง :

จำลองพฤติกรรมของคลาสตัวแปรโดยใช้คุณสมบัติ Objective-C

  1. ประกาศ / กำหนดตัวแปรสแตติกภายใน classA.m ดังนั้นมันจะสามารถเข้าถึงได้สำหรับเมธอด classA เท่านั้น (และทุกสิ่งที่คุณใส่ไว้ใน classA.m)

  2. เขียนทับวิธีการเริ่มต้นคลาส NSObject เพื่อเริ่มต้นเพียงครั้งเดียวตัวแปรคงที่ด้วยอินสแตนซ์ของ ClassB

  3. คุณจะสงสัยว่าทำไมฉันควรเขียนทับวิธีการเริ่มต้น NSObject เอกสารประกอบของ Apple เกี่ยวกับวิธีการนี้มีคำตอบ: "รันไทม์ส่งค่าเริ่มต้นไปยังแต่ละคลาสในโปรแกรมหนึ่งครั้งก่อนคลาสหรือคลาสใดก็ตามที่สืบทอดมาจากโปรแกรมนั้นจะถูกส่งข้อความแรกจากภายในโปรแกรม (ดังนั้นเมธอด อาจไม่ถูกเรียกใช้หากไม่ได้ใช้คลาส) ".

  4. รู้สึกอิสระที่จะใช้ตัวแปรแบบคงที่ภายในวิธีการ ClassA / คลาสใด ๆ

ตัวอย่างโค้ด :

ไฟล์: 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

การอ้างอิง :

  1. ตัวแปรคลาสอธิบายการเปรียบเทียบวัตถุประสงค์ -C และ C ++

3
คุณสามารถมีตัวแปรแบบคงที่ประเภท ClassA ภายใน classA.m?
goatlinks

6
นี่อาจเป็นคำถามที่งี่เง่า แต่สิ่งที่เกี่ยวกับการเปิดตัวของหน่วยความจำกล่าว ไม่สำคัญเพราะต้องใช้งานตราบเท่าที่แอปทำงานอยู่
samiq

1
@samiq ให้ตรวจสอบวัตถุประสงค์ - C: ทำไมต้องรักษาตัวแปรคงที่ . ไม่สามารถลบตัวชี้ไปยังวัตถุได้ แต่วัตถุนั้นสามารถทำได้ คุณอาจไม่ต้องการที่จะปล่อยมันเพราะคุณต้องการให้มันอยู่ได้นานเท่าที่แอพกำลังทำงานอยู่ แต่คุณจะประหยัดหน่วยความจำหากคุณปล่อยมันดังนั้นถ้าคุณรู้ว่าคุณไม่ต้องการมันอีกต่อไปคุณควร ปล่อยมัน
ma11hew28

5
หาก initialize () รับประกันว่าจะถูกเรียกเพียงครั้งเดียวทำไมคุณต้องมีเงื่อนไข "if (! classVariableName)"?
jb

23
@jamie initializeถูกเรียกหนึ่งครั้งสำหรับแต่ละคลาส (superclasses ก่อน subclasses) แต่ถ้า subclass ไม่แทนที่initializeคลาส parent initializeจะถูกเรียกอีกครั้ง ดังนั้นจำเป็นต้องมีตัวป้องกันหากคุณไม่ต้องการให้โค้ดนั้นรันสองครั้ง ดูการเริ่มต้นคลาสวัตถุในเอกสาร Objective-C ของ Apple
big_m

31

ตั้งแต่ Xcode 8 คุณสามารถกำหนดคุณสมบัติคลาสใน Obj-C สิ่งนี้ได้รับการเพิ่มให้ทำงานร่วมกับคุณสมบัติคงที่ของ Swift

Objective-C รองรับคุณสมบัติคลาสซึ่งทำงานร่วมกับคุณสมบัติชนิด Swift พวกเขาจะประกาศเป็น: @ คุณสมบัติ (ชั้น) NSString * someStringProperty; พวกเขาไม่เคยสังเคราะห์ (23891898)

นี่คือตัวอย่าง

@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

จากนั้นคุณสามารถเข้าถึงได้ดังนี้:

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

นี่เป็นคำอธิบายที่น่าสนใจมากที่ฉันใช้เป็นข้อมูลอ้างอิงในการแก้ไขคำตอบเก่านี้


2011 คำตอบ: (อย่าใช้สิ่งนี้มันแย่มาก)

หากคุณไม่ต้องการประกาศตัวแปรทั่วโลกจริงๆมีตัวเลือกอื่นอาจจะไม่มาก :-) แต่ทำงานได้ ... คุณสามารถประกาศวิธี "รับและตั้งค่า" เช่นนี้โดยมีตัวแปรแบบคงที่ภายใน:

+ (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;
}

ดังนั้นหากคุณต้องการรับค่าเพียงโทร:

NSString *testVal = [MyClass testHolder:nil]

จากนั้นเมื่อคุณต้องการตั้งค่า:

[MyClass testHolder:testVal]

ในกรณีที่คุณต้องการตั้งค่า pseudo-static-var นี้เป็นศูนย์คุณสามารถประกาศtestHolderดังนี้:

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

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

    return test;
}

และสองวิธีที่สะดวก:

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

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

หวังว่ามันจะช่วย! โชคดี.


เยี่ยม แต่ไม่ใช่ตัวแปรทั่วโลกเพราะไม่สามารถเข้าถึงได้จาก.mไฟล์อื่นและฉันคิดว่ามันดีสำหรับการเป็น "โลก" ภายในClass.mไฟล์
ma11hew28

29

ในไฟล์. m ของคุณคุณสามารถประกาศตัวแปรเป็นแบบคงที่:

static ClassName *variableName = nil;

จากนั้นคุณสามารถเริ่มต้นกับ+(void)initializeวิธีการของคุณ

โปรดทราบว่านี่เป็นตัวแปรแบบคงที่ C ธรรมดาและไม่คงที่ในความหมาย Java หรือ C # พิจารณา แต่จะให้ผลลัพธ์ที่คล้ายกัน


16

ในไฟล์. m ของคุณให้ประกาศตัวแปรโกลบอลของไฟล์:

static int currentID = 1;

จากนั้นในรูทีน init ของคุณให้อ้างอิงว่า:

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

หรือถ้ามันต้องการที่จะเปลี่ยนแปลงในเวลาอื่น ๆ (เช่นในวิธีการ openConnection ของคุณ) จากนั้นเพิ่มที่นั่น โปรดจำไว้ว่ามันไม่ปลอดภัยเธรดตามที่เป็นอยู่คุณจะต้องทำข้อมูลให้ตรงกัน (หรือดีกว่ายังใช้เพิ่มอะตอมมิก) หากอาจมีปัญหาเธรดใด ๆ


11

ดังที่ pgb กล่าวว่าไม่มี "ตัวแปรคลาส" เท่านั้น "ตัวแปรอินสแตนซ์" วิธีการทำตัวแปรคลาสเป็นตัวแปรโกลบอลแบบสแตติกภายในไฟล์. m ของคลาส "คงที่" ทำให้มั่นใจว่าตัวแปรไม่สามารถใช้นอกไฟล์นั้น (เช่นไม่สามารถ extern)


3

นี่คือตัวเลือก:

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

โปรดทราบว่าวิธีนี้จะเป็นวิธีเดียวในการเข้าถึง id ดังนั้นคุณจะต้องอัปเดตอย่างใดในรหัสนี้


2

(พูดอย่างเคร่งครัดไม่ใช่คำตอบสำหรับคำถาม แต่ในประสบการณ์ของฉันน่าจะมีประโยชน์เมื่อค้นหาตัวแปรคลาส)

วิธีการเรียนมักจะสามารถเล่นบทบาทหลายอย่างที่ตัวแปรระดับจะเป็นในภาษาอื่น ๆ (เช่นการกำหนดค่าที่เปลี่ยนแปลงระหว่างการทดสอบ):

@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

ตอนนี้วัตถุของการเรียนMyClsสายResource:changeSomething:กับสตริง@"Something general"เมื่อโทรไปdoTheThing:แต่วัตถุที่ได้มาจากการที่มีสตริงMySpecialCase@"Something specific"


0

คุณสามารถเปลี่ยนชื่อคลาสเป็น classA.mm และเพิ่มคุณสมบัติ C ++ ได้


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.