Objective-C 정적 클래스 레벨 변수


143

나는 고유 한 ID를 저장하는 클래스 영화를 가지고 있습니다. C #, Java 등에서 정적 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 initialize 메소드를 덮어 써야하는지 궁금 할 것입니다. 이 방법에 대한 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. Objective-C와 C ++ 접근법을 비교하는 클래스 변수

3
classA.m 내에 유형 ClassA의 정적 변수를 가질 수 있습니까?
염소 링크

6
이것은 어리석은 질문 일 수 있지만 메모리의 출시는 어떻습니까? 앱이 실행되는 동안 계속 살아야하기 때문에 중요하지 않습니까?
samiq

1
@samiq, Objective-C : 정적 변수를 유지하는 이유는 무엇입니까? . 객체에 대한 포인터는 삭제할 수 없지만 객체 자체는 삭제할 수 있습니다. 앱이 실행되는 한 대부분의 경우 원하기 때문에 릴리스하지 않으려 고하지만 릴리스하면 메모리가 절약되므로 더 이상 필요하지 않다는 것을 알고 있다면 그것을 풀어 라.
ma11hew28

5
initialize ()가 한 번만 호출되도록 보장 된 경우 조건부 "if (! classVariableName)"이 필요한 이유는 무엇입니까?
jb

23
@jamie initialize는 각 클래스 (하위 클래스 앞의 슈퍼 클래스)에 대해 한 번 호출되지만 서브 클래스가 재정의하지 않으면 initialize부모 클래스 initialize가 다시 호출됩니다. 따라서 해당 코드를 두 번 실행하지 않으려면 가드가 필요합니다. Apple의 Objective-C 문서에서 클래스 객체 초기화를 참조하십시오 .
big_m

31

Xcode 8부터 Obj-C에서 클래스 속성을 정의 할 수 있습니다. 이것은 Swift의 정적 속성과 상호 운용되도록 추가되었습니다.

Objective-C는 이제 Swift 유형 특성과 상호 운용되는 클래스 특성을 지원합니다. @property (클래스) 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 답변 : (이것을 사용하지 마십시오, 끔찍합니다)

정말로 전역 변수를 선언하고 싶지 않다면 또 다른 옵션이있을 수 있습니다. 정통은 아니지만 :-) 작동합니다 ... 정적 변수를 사용하여 이와 같은 "get & set"메소드를 선언 할 수 있습니다.

+ (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]

이 의사 정적 변수를 nil로 설정하려는 경우 다음 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가 말했듯이 "클래스 변수"는없고 "인스턴스 변수"만 있습니다. 클래스 변수를 수행하는 objective-c 방식은 클래스의 .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

u 클래스 이름을 classA.mm로 바꾸고 C ++ 기능을 추가 할 수 있습니다.


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.