Objective-C의 읽기 전용 속성?


93

내 인터페이스에서 다음과 같이 읽기 전용 속성을 선언했습니다.

 @property (readonly, nonatomic, copy) NSString* eventDomain;

속성을 오해하고있을 수도 있지만으로 선언 readonly하면 구현 ( .m) 파일 내부에서 생성 된 setter를 사용할 수 있지만 외부 엔터티는 값을 변경할 수 없다고 생각했습니다. 이 SO 질문 은 그것이 일어나야한다고 말합니다. 그것이 내가 추구하는 행동입니다. 그러나 표준 setter 또는 도트 구문을 사용하여 eventDomaininit 메서드 내부 를 설정 하려고하면 unrecognized selector sent to instance.오류가 발생합니다. 물론 나는 @synthesize재산을 소유하고 있습니다. 다음과 같이 사용하려고합니다.

 // inside one of my init methods
 [self setEventDomain:@"someString"]; // unrecognized selector sent to instance error

그래서 나는 readonly재산에 대한 선언을 오해하고 있습니까? 아니면 다른 일이 일어나고 있습니까?

답변:


116

세터도 필요하다고 컴파일러에 알려야합니다. 일반적인 방법은 .m 파일 의 클래스 확장자 에 넣는 것입니다 .

@interface YourClass ()

@property (nonatomic, copy) NSString* eventDomain;

@end

22
카테고리가 아닙니다. 이것은 클래스 확장입니다 (Eiko가 말했듯이). 비슷한 목적으로 사용되지만 분명히 다릅니다. 예를 들어 실제 카테고리에서는 위의 작업을 수행 할 수 없습니다.
bbum 2011 년

2
클래스 확장 속성이있는 클래스에서 상속 된 클래스는 해당 속성을 볼 수 없습니다. 즉 상속은 외부 인터페이스 만 볼 수 있습니다. 확인?
Yogev Shelly

5
그것은별로 공정하지 않다. 그것은 다를 수 있지만 그것은 "익명 카테고리」로서 알려져있다
MaxGabriel

3
이것은 공개 인터페이스에서 op의 초기 선언에 추가됩니까? 의미,이 클래스 확장 선언은 .h? 의 초기 선언을 재정의 합니까? 그렇지 않으면 이것이 공개 세터를 어떻게 노출하는지 알 수 없습니다. 감사합니다
Madbreaks

4
@Madbreaks 그것은 추가적이지만 .m 파일에 있습니다. 따라서 컴파일러는 해당 클래스에 대해 읽기 쓰기로 만드는 것을 알고 있지만 외부 사용은 여전히 ​​예상대로 읽기 전용으로 제한됩니다.
Eiko

38

에이코와 다른 사람들이 정답을주었습니다.

더 간단한 방법은 다음과 같습니다 . 전용 멤버 변수에 직접 액세스합니다.

헤더 .h 파일에서 :

@property (strong, nonatomic, readonly) NSString* foo;

구현 .m 파일에서 :

// inside one of my init methods
self->_foo = @"someString"; // Notice the underscore prefix of var name.

그게 전부입니다. 소란스럽지 않고 소란스럽지 않습니다.

세부

Xcode 4.4 및 LLVM 컴파일러 4.0 ( Xcode 4.4의 새로운 기능)부터는 다른 답변에서 논의 된 집안일을 엉망으로 만들 필요가 없습니다.

  • synthesize키워드
  • 변수 선언
  • 구현 .m 파일에서 속성을 다시 선언합니다.

속성을 선언 한 후 fooXcode가 underscore : 접두사로 명명 된 전용 멤버 변수를 추가했다고 가정 할 수 있습니다 _foo.

속성이 선언 된 readwrite경우 Xcode는 명명 된 getter 메서드 foo와 명명 된 setter를 생성합니다 setFoo. 이러한 메서드는 점 표기법 (my Object.myMethod)을 사용할 때 암시 적으로 호출됩니다. 속성이 선언 된 경우 readonlysetter가 생성되지 않습니다. 이는 밑줄로 명명 된 백업 변수 자체가 읽기 전용 이 아님을 의미합니다. readonly에는 세터 방법은 값을 설정 도트 표기법 따라서 합성하지시키고 단순히 수단 컴파일러 에러 실패. 컴파일러가 존재하지 않는 메서드 (세터) 호출을 중지하기 때문에 점 표기법이 실패합니다.

이 문제를 해결하는 가장 간단한 방법은 밑줄로 명명 된 멤버 변수에 직접 액세스하는 것입니다. 밑줄로 명명 된 변수를 선언하지 않아도 그렇게 할 수 있습니다! Xcode는 빌드 / 컴파일 프로세스의 일부로 해당 선언을 삽입하므로 컴파일 된 코드에는 실제로 변수 선언이 있습니다. 그러나 원래 소스 코드 파일에서는 이러한 선언을 볼 수 없습니다. 마술이 아니라 문법적인 설탕 .

사용 self->은 개체 / 인스턴스의 멤버 변수에 액세스하는 방법입니다. 이를 생략하고 var 이름 만 사용할 수 있습니다. 그러나 나는 내 코드를 자체 문서화하기 때문에 self + arrow를 사용하는 것을 선호합니다. 당신이 볼 때 self->_foo당신은 모호함없이 알고 _foo이 인스턴스의 멤버 변수입니다.


그건 그렇고, 재산 접근 자의 장단점과 직접 ivar 접근에 대한 논의는 Matt Neuberg 박사 의 Programming iOS 책 에서 읽을 수있는 사려 깊은 대우 입니다. 읽고 다시 읽는 것이 매우 도움이된다는 것을 알았습니다.


1
아마도 멤버 변수에 직접 (클래스없이) 접근해서는 안된다고 추가해야 할 것입니다! 이는 OOP (정보 숨김) 위반입니다.
HAS

2
@HAS 맞습니다. 여기 시나리오는 이 클래스 외부에서 볼 수없는 멤버 변수를 비공개로 설정하는 것입니다. 이것이 원래 포스터 질문의 요점입니다. 다른 클래스가 그것을 설정할 수 readonly없도록 속성을 선언하는 것입니다.
Basil Bourque

읽기 전용 속성을 만드는 방법에 대해이 게시물을보고있었습니다. 이것은 나를 위해 작동하지 않습니다. init에 값을 할당 할 수 있지만 나중에 할당을 통해 _variable을 변경할 수도 있습니다. 컴파일러 오류 또는 경고를 발생시키지 않습니다.
netskink

@BasilBourque "지속성"질문에 대한 유용한 편집을 해주셔서 대단히 감사합니다!
GhostCat

36

읽기 전용 속성으로 작업하는 또 다른 방법은 @synthesize를 사용하여 백업 저장소를 지정하는 것입니다. 예를 들면

@interface MyClass

@property (readonly) int whatever;

@end

그런 다음 구현에서

@implementation MyClass

@synthesize whatever = m_whatever;

@end

그러면 m_whatever멤버 변수이므로 메서드를 설정할 수 있습니다 .


지난 며칠 동안 제가 깨달은 또 다른 흥미로운 점은 다음과 같은 하위 클래스에서 쓰기 가능한 읽기 전용 속성을 만들 수 있다는 것입니다.

(헤더 파일에서)

@interface MyClass
{
    @protected
    int m_propertyBackingStore;
}

@property (readonly) int myProperty;

@end

그런 다음 구현에서

@synthesize myProperty = m_propertyBackingStore;

헤더 파일의 선언을 사용하므로 하위 클래스는 읽기 전용을 유지하면서 속성 값을 업데이트 할 수 있습니다.

데이터 숨김 및 캡슐화 측면에서 약간 유감스럽게도.


1
당신이 설명한 첫 번째 방법은 받아 들여진 대답보다 다루기가 더 쉽습니다. "@synthesize foo1, foo2, foo3;" .m에서 모든 것이 좋습니다.
sudo

20

기존 클래스 사용자 정의를 참조하십시오.iOS 문서에서 .

readonly 속성이 읽기 전용임을 나타냅니다. 읽기 전용을 지정하는 경우 @implementation에 getter 메소드 만 필요합니다. 구현 블록에서 @synthesize를 사용하면 getter 메서드 만 합성됩니다. 또한 도트 구문을 사용하여 값을 할당하려고하면 컴파일러 오류가 발생합니다.

읽기 전용 속성에는 getter 메서드 만 있습니다. 여전히 속성의 클래스 내에서 직접 또는 키 값 코딩을 사용하여 백업 ivar를 설정할 수 있습니다.


9

다른 질문을 오해하고 있습니다. 이 질문 에는 다음과 같이 선언 된 클래스 확장이 있습니다.

@interface MYShapeEditorDocument ()
@property (readwrite, copy) NSArray *shapesInOrderBackToFront;
@end

이것이 클래스의 구현에서만 볼 수있는 setter를 생성하는 것입니다. 따라서 Eiko가 말했듯이 클래스 확장을 선언하고 속성 선언을 재정 의하여 컴파일러에게 클래스 내에서만 setter를 생성하도록 지시해야합니다.


5

가장 짧은 해결책은 다음과 같습니다.

MyClass.h

@interface MyClass {

  int myProperty;

}

@property (readonly) int myProperty;

@end

MyClass.h

@implementation MyClass

@synthesize myProperty;

@end

2

속성이 읽기 전용으로 정의 된 경우 이는 클래스 내부 또는 다른 클래스에서 외부 적으로 사용할 수있는 setter가 사실상 없다는 것을 의미합니다. (예 : 말이되는 경우에만 "게터"가 있습니다.)

소리에서 보면 private로 표시된 일반 읽기 / 쓰기 속성이 필요합니다.이 속성은 다음과 같이 인터페이스 파일에서 클래스 변수를 private으로 설정하여 얻을 수 있습니다.

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