Objective-C에서 -init 메소드를 개인용으로 만들 수 있습니까?
-init
Objective-C에서 클래스 의 메소드 를 숨기거나 (비공개로)해야합니다 .
어떻게해야합니까?
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
메소드가 싱글 톤에 액세스하는 방법이 되도록 코드를 작성 하고 헤더에 싱글 톤 인스턴스를 가져 오는 방법으로 문서화하십시오. 대부분의 경우 필요한 것입니다.
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;
unavailable
init 호출시 컴파일러 오류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
사용 NSAssert
NSInternalInconsistencyException을 던져 메시지를 표시합니다 :
- (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]
is needed because the object was already alloc
ated. When using ARC the compiler will call it for you. In any case, not something to worry when you are about to intentionally stop execution.
objc_designated_initializer
In case you intend to disable init
to force the use of a designated initializer, there is an attribute for that:
-(instancetype)myOwnInit NS_DESIGNATED_INITIALIZER;
This generates a warning unless any other initializer method calls myOwnInit
internally. Details will be published in Adopting Modern Objective-C after next Xcode release (I guess).
Apple has started using the following in their header files to disable the init constructor:
- (instancetype)init NS_UNAVAILABLE;
This correctly displays as a compiler error in Xcode. Specifically, this is set in several of their HealthKit header files (HKUnit is one of them).
If you are talking about the default -init method then you can't. It's inherited from NSObject and every class will respond to it with no warnings.
You could create a new method, say -initMyClass, and put it in a private category like Matt suggests. Then define the default -init method to either raise an exception if it's called or (better) call your private -initMyClass with some default values.
One of the main reasons people seem to want to hide init is for singleton objects. If that's the case then you don't need to hide -init, just return the singleton object instead (or create it if it doesn't exist yet).
Put this in header file
- (id)init UNAVAILABLE_ATTRIBUTE;
That depends on what you mean by "make private". In Objective-C, calling a method on an object might better be described as sending a message to that object. There's nothing in the language that prohibits a client from calling any given method on an object; the best you can do is not declare the method in the header file. If a client nevertheless calls the "private" method with the right signature, it will still execute at runtime.
That said, the most common way to create a private method in Objective-C is to create a Category in the implementation file, and declare all of the "hidden" methods in there. Remember that this won't truly prevent calls to init
from running, but the compiler will spit out warnings if anyone tries to do this.
MyClass.m
@interface MyClass (PrivateMethods)
- (NSString*) init;
@end
@implementation MyClass
- (NSString*) init
{
// code...
}
@end
There's a decent thread on MacRumors.com about this topic.
well the problem why you can't make it "private/invisible" is cause the init method gets send to id (as alloc returns an id) not to YourClass
Note that from the point of the compiler (checker) an id could potencialy respond to anything ever typed (it can't check what really goes into the id at runtime), so you could hide init only when nothing nowhere would (publicly = in header) use a method init, than the compile would know, that there is no way for id to respond to init, since there is no init anywhere (in your source, all libs etc...)
so you cannot forbid the user to pass init and get smashed by the compiler... but what you can do, is to prevent the user from getting a real instance by calling a init
simply by implementing init, which returns nil and have an (private / invisible) initializer which name somebody else won't get (like initOnce, initWithSpecial ...)
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;
}
Note : that somebody could do this
SomeClass * c = [[SomeClass alloc] initOnce];
and it would in fact return a new instance, but if the initOnce would nowhere in our project be publicly (in header) declared, it would generate a warning (id might not respond ...) and anyway the person using this, would need to know exactly that the real initializer is the initOnce
we could prevent this even further, but there is no need
You can declare any method to be not available using NS_UNAVAILABLE
.
So you can put these lines below your @interface
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;
Even better define a macro in your prefix header
#define NO_INIT \
- (instancetype)init NS_UNAVAILABLE; \
+ (instancetype)new NS_UNAVAILABLE;
and
@interface YourClass : NSObject
NO_INIT
// Your properties and messages
@end
I have to mention that placing assertions and raising exceptions to hide methods in the subclass has a nasty trap for the well-intended.
I would recommend using __unavailable
as Jano explained for his first example.
Methods can be overridden in subclasses. This means that if a method in the superclass uses a method that just raises an exception in the subclass, it probably won't work as intended. In other words, you've just broken what used to work. This is true with initialization methods as well. Here is an example of such rather common implementation:
- (SuperClass *)initWithParameters:(Type1 *)arg1 optional:(Type2 *)arg2
{
...bla bla...
return self;
}
- (SuperClass *)initWithLessParameters:(Type1 *)arg1
{
self = [self initWithParameters:arg1 optional:DEFAULT_ARG2];
return self;
}
Imagine what happens to -initWithLessParameters, if I do this in the subclass:
- (SubClass *)initWithParameters:(Type1 *)arg1 optional:(Type2 *)arg2
{
[self release];
[super doesNotRecognizeSelector:_cmd];
return nil;
}
This implies that you should tend to use private (hidden) methods, especially in initialization methods, unless you plan to have the methods overridden. But, this is another topic, since you don't always have full control in the implementation of the superclass. (This makes me question the use of __attribute((objc_designated_initializer)) as bad practice, although I haven't used it in depth.)
It also implies that you can use assertions and exceptions in methods that must be overridden in subclasses. (The "abstract" methods as in Creating an abstract class in Objective-C )
And, don't forget about the +new class method.
'IT' 카테고리의 다른 글
Android의 전체 화면 대화 상자 (0) | 2020.06.19 |
---|---|
PHP 앱이 404 오류를 보내지 않는 이유는 무엇입니까? (0) | 2020.06.19 |
UIView 주위의 점선 경계선 (0) | 2020.06.19 |
낙타 사건에 찬성하여 밑줄을 극복하는 잭슨 (0) | 2020.06.19 |
Android에서 현재 날짜를 얻으려면 어떻게해야합니까? (0) | 2020.06.19 |