Objective-C에서 클래스 수준 속성을 어떻게 선언합니까?


205

어쩌면 이것은 분명하지만 Objective-C에서 클래스 속성을 선언하는 방법을 모르겠습니다.

클래스별로 사전을 캐시하고 클래스에 사전을 넣는 방법이 궁금합니다.

답변:


190

속성은 Objective-C에서 특정 의미를 갖지만 정적 변수와 동등한 것을 의미한다고 생각합니까? 예를 들어 모든 유형의 Foo에 대해 하나의 인스턴스 만?

Objective-C에서 클래스 함수를 선언하려면 대신 + 접두사를 사용하므로 구현은 다음과 같습니다.

// Foo.h
@interface Foo {
}

+ (NSDictionary *)dictionary;

// Foo.m
+ (NSDictionary *)dictionary {
  static NSDictionary *fooDict = nil;
  if (fooDict == nil) {
    // create dict
  }
  return fooDict;
}

23
이게 옳은 거니? 사전 메서드의 첫 번째 줄이 항상 매번 사전을 다시 생성하므로 fooDict를 nil로 설정하지 않습니까?
PapillonUK


59
정적 라인 NSDictionary * fooDict = nil; 한 번만 실행됩니다! 여러 번 호출 된 메소드에서도 정적 변수가이 이름으로 존재하면 키워드 static을 사용한 선언 (및이 예제에서 초기화)이 무시됩니다.
Binarian

3
@ BenC.R. Leggiero 네, 물론입니다. .-accessor 구문이 목표 - C의 속성에 연결되지 않은, 그것은 그냥 컴파일 된 바로 가기 어떤 방법에 대한 그 어떤 인수를 고려하지 않고 반환 뭔가. 이 경우, 나는 그것을 선호한다. 나는 .클라이언트 코드가 무언가를 얻거나 의도하지 않은 사용에 대해 구문을 선호 한다 (구현 코드가 한번 무언가를 만들거나 부작용을 수행하더라도) . 사용량 .이 많은 구문을 사용 하면 코드를보다 쉽게 ​​읽을 수 있습니다. […]s가 있으면 가져 오기에서 .구문을 대신 사용할 때 중요한 작업이 수행되고 있음을 의미 합니다.
슬립 D. 톰슨

4
Alex Nolasco의 답변을 살펴보십시오. Xcode 8 릴리스 이후 클래스 속성을 사용할 수 있습니다. stackoverflow.com/a/37849467/6666611
n3wbie

112

이 솔루션을 사용하고 있습니다 :

@interface Model
+ (int) value;
+ (void) setValue:(int)val;
@end

@implementation Model
static int value;
+ (int) value
{ @synchronized(self) { return value; } }
+ (void) setValue:(int)val
{ @synchronized(self) { value = val; } }
@end

그리고 Singleton 패턴을 대체하는 데 매우 유용하다는 것을 알았습니다.

사용하려면 간단히 점 표기법으로 데이터에 액세스하십시오.

Model.value = 1;
NSLog(@"%d = value", Model.value);

3
정말 멋지다. 그러나 지구상 self에서 수업 방법 안에서 무엇을 의미합니까?
Todd Lehman

8
@ToddLehman self메시지를받은 객체입니다 . 그리고 이후 클래스도 객체이다 이 경우에, self수단Model
spooki

6
게터가 왜 필요 @synchronized할까요?
Matt Kantor

1
이것은 실제로 작동하는 것이 정말 멋지다. 실제와 똑같이 작동하는 클래스 수준 속성을 표시 할 수 있습니다. self 동기화는 속성 선언에서 'atomic'을 사용하는 것과 같으며 'nonatomic'버전을 원한다면 생략 할 수 있습니까? 또한 기본적으로 '_'로 백업 변수의 이름을 지정하는 것을 고려하고 getter / setter에서 self.value를 반환 / 설정하면 무한 재귀가 발생하기 때문에 가독성이 향상됩니다. 제 생각에는 이것이 정답입니다.
피터 Segerblom

3
멋지지만 ... 정적 멤버 1 개를 만드는 10 줄의 코드? 어떤 해킹. 애플은 이것을 기능으로 만들어야한다.
존 헨켈

92

WWDC 2016 / XCode 8 ( LLVM 세션 @ 5 : 05 의 새로운 기능 )에 나와 있습니다. 클래스 속성은 다음과 같이 선언 할 수 있습니다

@interface MyType : NSObject
@property (class) NSString *someString;
@end

NSLog(@"format string %@", MyType.someString);

클래스 속성은 절대 합성되지 않습니다.

@implementation
static NSString * _someString;
+ (NSString *)someString { return _someString; }
+ (void)setSomeString:(NSString *)newString { _someString = newString; }
@end

8
이것은 접근 자 선언을위한 설탕 일 뿐이 라는 것이 명백해야합니다 . 언급했듯이 속성은 합성되지 않습니다. ( static) 변수는 여전히 선언되고 사용되어야하며 메서드는 이전과 같이 명시 적으로 구현되어야합니다. 도트 구문도 이미 이전에 작동했습니다. 결국 이것은 실제보다 더 큰 것처럼 들립니다.
jscs

6
큰 의미는 ()를 사용하지 않고 Swift 코드에서 싱글 톤에 액세스 할 수 있으며 형식 접미사가 규칙에 따라 제거된다는 것입니다. 예 : XYZMyClass.sharedMyClass () 대신 XYZMyClass.shared (Swift 3)
Ryan

스레드 안전하지 않습니다. 이것이 코드의 다른 스레드에서 변경 될 수 있다면 잠재적 인 경쟁 조건을 처리해야합니다.
smileBot

클래스를 소비하기에 좋은 깔끔한 인터페이스이지만 여전히 많은 작업입니다. 정적 블록으로 이것을 시도했지만별로 재미 있지 않았습니다. 실제로 정적을 사용하는 것이 훨씬 쉬웠습니다.
Departamento B

1
구현은 쉽게 토큰 dispatch_once를 사용하여, 스레드 안전 "싱글"행동 향상 될 수있다 -하지만 그렇지 않으면 솔루션은 제대로 답을 보여줍니다
은 Motti Shneor을

63

클래스 레벨에 해당하는 클래스를 찾고 있다면 @property"아무것도 없습니다"라는 대답이 있습니다. 그러나 @property어쨌든 구문 설탕 만 기억하십시오 . 적절한 이름의 객체 메소드 만 생성합니다.

다른 사람들과 마찬가지로 정적 변수에 액세스하는 클래스 메소드를 작성하여 구문이 약간 다릅니다.


생각의 속성조차도 구문 적 임에도 불구하고 [MyClass 클래스] 대신 MyClass.class와 같은 것에 도트 구문을 사용할 수 있다는 것은 여전히 ​​좋은 일입니다.
Zaky German

4
@ZakyGerman 당신은 할 수 있습니다! UIDevice.currentDevice.identifierForVendor나를 위해 작동합니다.
tc.

1
@tc. 감사합니다! 지금 바보를 채우고 있습니다. 어떤 이유로 든 과거에 시도했지만 작동하지 않았다고 확신했습니다. 우연히 새로운 기능입니까?
Zaky German 22

1
@ZakyGerman 최소한 1 년 또는 2 년 동안 수업 방법으로 일했습니다. getter / setter 메소드에 예상 유형이있는 경우 항상 인스턴스 메소드에서 작동했다고 생각합니다.
tc.

21

스레드 안전 방법은 다음과 같습니다.

// Foo.h
@interface Foo {
}

+(NSDictionary*) dictionary;

// Foo.m
+(NSDictionary*) dictionary
{
  static NSDictionary* fooDict = nil;

  static dispatch_once_t oncePredicate;

  dispatch_once(&oncePredicate, ^{
        // create dict
    });

  return fooDict;
}

이러한 편집은 fooDict가 한 번만 작성되도록합니다.

에서 애플 문서 : "dispatch_once - 블록 개체를 한 번만 응용 프로그램의 평생을 실행합니다."


3
정적 NSDictionary가 + (NSDictionary *) 사전 메서드의 첫 번째 줄에서 초기화 될 수 있고 정적이기 때문에 dispatch_once 코드는 관련이 없습니까? 어쨌든 한 번만 초기화됩니다.
jcpennypincher

@jcpennypincher 정적 선언과 같은 줄에서 사전을 초기화하려고하면 다음과 같은 컴파일러 오류가 발생 Initializer element is not a compile-time constant합니다.
George WS

@GeorgeWS 함수의 결과 (alloc과 init는 함수 임)로 초기화하려고하기 때문에 오류가 발생합니다. nil로 초기화 한 다음 if (obj == nil)를 추가하고 초기화하면 괜찮습니다.
Rob

1
Rob, 스레드 안전하지 않습니다. 여기에 제시된 코드가 가장 좋습니다.
Ian Ollmann

이 답변이 완벽하고 스레드 안전하기 때문에이 답변이 가장 좋습니다. 그러나 구현에 더 잘 대처하는 반면 op의 질문은 클래스 속성 선언에 관한 것이지 구현이 아닙니다 (구현과 관련이 없음). 어쨌든 고마워
Motti Shneor 2007 년

11

Xcode 8부터 Objective-C는 이제 클래스 속성을 지원합니다.

@interface MyClass : NSObject
@property (class, nonatomic, assign, readonly) NSUUID* identifier;
@end

클래스 속성은 합성되지 않으므로 직접 구현해야합니다.

@implementation MyClass
static NSUUID*_identifier = nil;

+ (NSUUID *)identifier {
  if (_identifier == nil) {
    _identifier = [[NSUUID alloc] init];
  }
  return _identifier;
}
@end

클래스 이름에 일반 도트 구문을 사용하여 클래스 속성에 액세스합니다.

MyClass.identifier;

7

속성은 클래스가 아닌 객체에만 값을 갖습니다.

클래스의 모든 객체에 무언가를 저장해야하는 경우 전역 변수를 사용해야합니다. static구현 파일에서 선언하여 숨길 수 있습니다 .

또한 객체 간의 특정 관계 사용을 고려할 수도 있습니다. 마스터의 역할을 클래스의 특정 객체에 부여하고 다른 객체를이 마스터에 연결합니다. 마스터는 사전을 간단한 속성으로 보유합니다. Cocoa 응용 프로그램의 뷰 계층 구조에 사용되는 트리와 같은 트리를 생각합니다.

또 다른 옵션은 '클래스'사전과이 사전과 관련된 모든 개체 집합으로 구성된 전용 클래스의 개체를 만드는 것입니다. 이것은 NSAutoreleasePool코코아 와 같은 것 입니다.


7

Xcode 8부터는 Berbie의 답변에 따라 클래스 속성 속성을 사용할 수 있습니다 .

그러나 구현시 iVar 대신 정적 변수를 사용하여 클래스 특성에 대한 클래스 getter 및 setter를 정의해야합니다.

Sample.h

@interface Sample: NSObject
@property (class, retain) Sample *sharedSample;
@end

Sample.m

@implementation Sample
static Sample *_sharedSample;
+ ( Sample *)sharedSample {
   if (_sharedSample==nil) {
      [Sample setSharedSample:_sharedSample];
   }
   return _sharedSample;
}

+ (void)setSharedSample:(Sample *)sample {
   _sharedSample = [[Sample alloc]init];
}
@end

2

클래스 수준 속성이 많은 경우 싱글 톤 패턴이 순서대로 표시 될 수 있습니다. 이 같은:

// Foo.h
@interface Foo

+ (Foo *)singleton;

@property 1 ...
@property 2 ...
@property 3 ...

@end

// Foo.m

#import "Foo.h"

@implementation Foo

static Foo *_singleton = nil;

+ (Foo *)singleton {
    if (_singleton == nil) _singleton = [[Foo alloc] init];

    return _singleton;
}

@synthesize property1;
@synthesize property2;
@synthesise property3;

@end

이제 다음과 같이 클래스 레벨 속성에 액세스하십시오.

[Foo singleton].property1 = value;
value = [Foo singleton].property2;

4
이 싱글 톤 구현은 스레드로부터 안전하지 않습니다. 사용하지 마십시오
klefevre

1
물론 스레드 안전은 아닙니다. 스레드 안전을 가장 먼저 언급 한 사람이며 기본적으로 스레드 안전이 아닌 컨텍스트 (예 : 단일 스레드)의 스레드 안전 과부하는 의미가 없습니다.
Pedro Borges

dispatch_once여기 를 사용하는 것은 매우 쉽습니다 .
Ian MacDonald

PO는 구현이 아닌 선언 측에 대한 답변을 원했으며 제안 된 구현조차 불완전합니다 (스레드 안전하지 않음).
Motti Shneor

-3

[이 솔루션은 간단합니다.] Swift 클래스에서 정적 변수를 만든 다음 Objective-C 클래스에서 호출 할 수 있습니다.


1
OP는 Swift에서 정적 속성을 만드는 방법을 묻지 않았지만 문제를 해결하지 못했습니다.
Nathan F.

또는 오히려 문제를 해결했지만 질문에 대답하지 않았습니다. 그 가치가 있는지 확실하지 않은지 투표하십시오 ...
AmitaiB
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.