-initObjective-C에서 클래스 의 메소드 를 숨기거나 (비공개로)해야합니다 .
어떻게해야합니까?
NS_UNAVAILABLE여전히 발신자 init가을 통해 간접적으로 호출하도록 허용합니다 new. 단순히 init반환 nil을 재정의 하면 두 경우 모두 처리됩니다.
-initObjective-C에서 클래스 의 메소드 를 숨기거나 (비공개로)해야합니다 .
어떻게해야합니까?
NS_UNAVAILABLE여전히 발신자 init가을 통해 간접적으로 호출하도록 허용합니다 new. 단순히 init반환 nil을 재정의 하면 두 경우 모두 처리됩니다.
답변:
Smalltalk와 마찬가지로 Objective-C에는 "비공개"대 "공용"방법에 대한 개념이 없습니다. 모든 메시지는 언제든지 모든 개체에 보낼 수 있습니다.
당신이 할 수있는 일은 메소드가 호출 된 NSInternalInconsistencyException경우 throw하는 것입니다 -init.
- (id)init {
[self release];
@throw [NSException exceptionWithName:NSInternalInconsistencyException
reason:@"-init is not a valid initializer for the class Foo"
userInfo:nil];
return nil;
}
실제로 훨씬 더 나은 다른 대안은 -init가능하다면 수업에 합리적으로 행동 하도록 하는 것입니다.
싱글 톤 객체가 "확보"되도록하기 위해이 작업을 수행하는 경우 귀찮게하지 마십시오. 특히, 신경 쓰지 않는다 "재정의 +allocWithZone:, -init, -retain, -release"싱글 톤을 만드는 방법. 실제로는 항상 불필요하며 실질적인 이점없이 합병증을 추가하는 것입니다.
대신, +sharedWhatever메소드가 싱글 톤에 액세스하는 방법이 되도록 코드를 작성 하고 헤더에 싱글 톤 인스턴스를 얻는 방법으로 문서화하십시오. 그것이 대부분의 경우에 필요한 전부입니다.
alloc하고 init기능을 수행 할 수 있습니다. 이것이 OO 의 캡슐화 원칙의 본질입니다 . 다른 클래스가 액세스하거나 액세스 할 필요가없는 것을 API에 숨 깁니다. 당신은 모든 것을 공개하지 않고 인간이 모든 것을 추적하기를 기대합니다.
NS_UNAVAILABLE- (instancetype)init NS_UNAVAILABLE;
사용 불가능한 속성의 짧은 버전입니다. 그것은 macOS 10.7 과 iOS 5 에서 처음 등장했습니다 . NSObjCRuntime.h에로 정의되어 있습니다 #define NS_UNAVAILABLE UNAVAILABLE_ATTRIBUTE.
ObjC 코드가 아닌 Swift 클라이언트에 대해서만 메소드 를 비활성화 하는 버전이 있습니다.
- (instancetype)init NS_SWIFT_UNAVAILABLE;
unavailableinit 호출시 컴파일러 오류unavailable 를 생성 하려면 속성을 헤더에 추가하십시오 .
-(instancetype) init __attribute__((unavailable("init not available")));

이유가 없다면 __attribute__((unavailable)), 또는 __unavailable:
-(instancetype) __unavailable init;
doesNotRecognizeSelector:doesNotRecognizeSelector:NSInvalidArgumentException을 발생시키는 데 사용 합니다. "런타임 시스템은 객체가 응답하거나 전달할 수없는 aSelector 메시지를 수신 할 때마다이 메소드를 호출합니다."
- (instancetype) init {
[self release];
[super doesNotRecognizeSelector:_cmd];
return nil;
}
NSAssert사용 NSAssertNSInternalInconsistencyException을 던져 메시지를 표시합니다 :
- (instancetype) init {
[self release];
NSAssert(false,@"unavailable, use initWithBlah: instead");
return nil;
}
raise:format:raise:format:자신의 예외를 던지기 위해 사용하십시오 .
- (instancetype) init {
[self release];
[NSException raise:NSGenericException
format:@"Disabled. Use +[[%@ alloc] %@] instead",
NSStringFromClass([self class]),
NSStringFromSelector(@selector(initWithStateDictionary:))];
return nil;
}
[self release]개체가 이미 alloc먹었 기 때문에 필요합니다 . ARC를 사용할 때 컴파일러가이를 호출합니다. 어쨌든 의도적으로 실행을 중지하려고 할 때 걱정할 것이 없습니다.
objc_designated_initializerinit지정된 이니셜 라이저를 강제 로 사용하지 않으 려면 다음과 같은 속성이 있습니다.
-(instancetype)myOwnInit NS_DESIGNATED_INITIALIZER;
다른 초기화 메소드가 myOwnInit내부적으로 호출하지 않으면 경고가 생성됩니다 . 세부 사항은 다음 Xcode 릴리스 이후 Modern Objective-C 채택에 게시 될 것 입니다.
init. 이 방법이 유효하지 않은 경우 왜 객체를 초기화합니까? 또한 예외가 발생하면 init*개발자에게 올바른 방법을 전달하는 일부 사용자 지정 메시지를 지정할 수 있지만의 경우에는 해당 옵션이 없습니다 doesNotRecognizeSelector.
Apple은 헤더 파일에서 다음을 사용하여 초기화 생성자를 비활성화했습니다.
- (instancetype)init NS_UNAVAILABLE;
이것은 Xcode에서 컴파일러 오류로 올바르게 표시됩니다. 특히 이것은 여러 HealthKit 헤더 파일에 설정되어 있습니다 (HKUnit은 그 중 하나임).
기본 -init 메소드에 대해 이야기하고 있다면 할 수 없습니다. NSObject에서 상속되며 모든 클래스는 경고없이 응답합니다.
-initMyClass와 같은 새로운 메소드를 작성하여 Matt이 제안한 개인 카테고리에 넣을 수 있습니다. 그런 다음 기본 -init 메소드를 정의하여 예외가 호출 된 경우 예외를 발생 시키거나 개인용 -initMyClass를 일부 기본값으로 호출하십시오.
사람들이 init를 숨기고 싶어하는 주된 이유 중 하나는 싱글 톤 객체 입니다. 이 경우 -init를 숨길 필요가 없으며 대신 싱글 톤 객체를 반환하십시오 (또는 아직 존재하지 않는 경우 생성하십시오).
이것을 헤더 파일에 넣으십시오.
- (id)init UNAVAILABLE_ATTRIBUTE;
를 사용하여 사용할 수없는 메소드를 선언 할 수 있습니다 NS_UNAVAILABLE.
따라서 @interface 아래에이 줄을 넣을 수 있습니다
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;
접두사 헤더에 매크로를 더 잘 정의하십시오.
#define NO_INIT \
- (instancetype)init NS_UNAVAILABLE; \
+ (instancetype)new NS_UNAVAILABLE;
과
@interface YourClass : NSObject
NO_INIT
// Your properties and messages
@end
"만들기"라는 의미에 따라 다릅니다. Objective-C에서는 객체에서 메소드를 호출하는 것이 해당 객체로 메시지를 보내는 것으로 더 잘 설명 될 수 있습니다. 클라이언트가 객체에 대해 주어진 메소드를 호출하는 것을 금지하는 언어는 없습니다. 헤더 파일에서 메소드를 선언하지 않는 것이 가장 좋습니다. 그럼에도 불구하고 클라이언트가 올바른 서명으로 "private"메소드를 호출하면 여전히 런타임에 실행됩니다.
즉, Objective-C에서 개인용 메소드를 작성하는 가장 일반적인 방법 은 구현 파일에서 카테고리 를 작성하고 모든 "숨겨진"메소드를 선언하는 것입니다. 이렇게해도 호출이 init실행되는 것을 막을 수는 없지만, 누군가이 작업을 시도하면 컴파일러에서 경고를 내 보냅니다.
MyClass.m
@interface MyClass (PrivateMethods)
- (NSString*) init;
@end
@implementation MyClass
- (NSString*) init
{
// code...
}
@end
MacRumors.com에는이 주제와 관련 하여 적절한 스레드 가 있습니다 .
글쎄, 왜 당신이 그것을 "비공개 / 보이지 않게"만들 수없는 문제는 init 메소드가 YourClass가 아닌 id로 (alloc이 id를 리턴하기 때문에) 보내 게 만드는 원인이다
컴파일러 (체커)의 시점에서 ID는 입력 한 모든 것에 잠재적으로 반응 할 수 있으므로 (런타임에 실제로 ID에 들어가는 것을 확인할 수는 없습니다), 아무 것도 (공개적으로 = in 헤더) 컴파일이 알 수있는 것보다 init 메소드를 사용하십시오 .init이 어디에도 없기 때문에 id가 init에 응답 할 수있는 방법이 없습니다 (소스, 모든 라이브러리 등)
따라서 사용자가 init를 전달하고 컴파일러에 의해 스매시되는 것을 금지 할 수는 없지만 init를 호출하여 사용자가 실제 인스턴스를 얻지 못하도록 할 수 있습니다
단순히 nil을 반환하고 다른 사람이 얻지 못하는 이름 (initOnce, initWithSpecial ...)을 갖는 (private / invisible) 이니셜 라이저를 갖는 init을 구현하면됩니다.
static SomeClass * SInstance = nil;
- (id)init
{
// possibly throw smth. here
return nil;
}
- (id)initOnce
{
self = [super init];
if (self) {
return self;
}
return nil;
}
+ (SomeClass *) shared
{
if (nil == SInstance) {
SInstance = [[SomeClass alloc] initOnce];
}
return SInstance;
}
참고 : 누군가가 이것을 할 수 있음
SomeClass * c = [[SomeClass alloc] initOnce];
실제로 새 인스턴스를 반환하지만 initOnce가 프로젝트의 어느 곳에서도 (헤더로) 공개적으로 선언되지 않으면 경고 (ID가 응답하지 않을 수 있음 ...)를 생성하고 어쨌든 이것을 사용하는 사람은 필요합니다. 실제 이니셜 라이저가 initOnce임을 정확히 아는 것
우리는 이것을 더 예방할 수 있지만, 필요는 없습니다
서브 클래스에서 메소드를 숨기기 위해 어설 션을 배치하고 예외를 발생시키는 것은 좋은 의도를 가진 함정이 있다는 것을 언급해야합니다.
Jano가 첫 번째 예제에서 설명한__unavailable 대로 사용 하는 것이 좋습니다. .
서브 클래스에서 메소드를 대체 할 수 있습니다. 이는 수퍼 클래스의 메소드가 서브 클래스에서 예외를 발생시키는 메소드를 사용하는 경우 의도 한대로 작동하지 않을 수 있음을 의미합니다. 다시 말해, 당신은 이전에 사용했던 것을 깨뜨 렸습니다. 초기화 방법에서도 마찬가지입니다. 이러한 일반적인 구현의 예는 다음과 같습니다.
- (SuperClass *)initWithParameters:(Type1 *)arg1 optional:(Type2 *)arg2
{
...bla bla...
return self;
}
- (SuperClass *)initWithLessParameters:(Type1 *)arg1
{
self = [self initWithParameters:arg1 optional:DEFAULT_ARG2];
return self;
}
서브 클래스에서 이것을 수행하면 -initWithLessParameters에 어떤 일이 발생하는지 상상해보십시오.
- (SubClass *)initWithParameters:(Type1 *)arg1 optional:(Type2 *)arg2
{
[self release];
[super doesNotRecognizeSelector:_cmd];
return nil;
}
즉, 메서드를 재정의하지 않는 한 특히 초기화 메서드에서 개인 (숨겨진) 메서드를 사용하는 경향이 있습니다. 그러나 슈퍼 클래스 구현을 항상 완벽하게 제어 할 수있는 것은 아니기 때문에 이것은 또 다른 주제입니다. (이로 인해 깊이를 사용하지는 않았지만 __attribute ((objc_designated_initializer))를 나쁜 습관으로 사용하는 것에 의문을 제기합니다.)
또한 서브 클래스에서 재정의해야하는 메소드에서 어설 션 및 예외를 사용할 수 있음을 의미합니다. Objective-C에서 추상 클래스 작성 과 같은 "추상적 인"메소드 )
그리고 + new class 메소드를 잊지 마십시오.
NS_UNAVAILABLE. 나는 일반적으로이 접근법을 사용할 것을 권장합니다. OP가 승인 된 답변을 수정하는 것을 고려할 것입니까? 여기에있는 다른 답변은 많은 유용한 세부 사항을 제공하지만 이것을 달성하는 바람직한 방법은 아닙니다.