it-swarm.com.de

Ist es möglich, die Methode -init in Objective-C als privat zu kennzeichnen?

Ich muss die -init - Methode meiner Klasse in Objective-C verbergen (privat machen).

Wie kann ich das machen?

140
lajos

Objective-C kennt wie Smalltalk keine "privaten" oder "öffentlichen" Methoden. Jede Nachricht kann jederzeit an jedes Objekt gesendet werden.

Sie können ein NSInternalInconsistencyException auslösen, wenn Ihre -init - Methode aufgerufen wird:

- (id)init {
    [self release];
    @throw [NSException exceptionWithName:NSInternalInconsistencyException
                                   reason:@"-init is not a valid initializer for the class Foo"
                                 userInfo:nil];
    return nil;
}

Die andere Alternative - die in der Praxis wahrscheinlich weitaus besser ist - besteht darin, -init Zu veranlassen, für Ihre Klasse etwas Vernünftiges zu tun, wenn dies überhaupt möglich ist.

Wenn Sie versuchen, dies zu tun, weil Sie versuchen, sicherzustellen, dass ein Singleton-Objekt verwendet wird, stören Sie sich nicht. Beachten Sie insbesondere die Methode "Überschreiben von +allocWithZone:, -init, -retain, -release" Zum Erstellen von Singletons. Es ist praktisch immer unnötig und fügt nur Komplikationen hinzu, ohne dass ein wesentlicher Vorteil entsteht.

Schreiben Sie stattdessen einfach Ihren Code so, dass Sie mit der Methode +sharedWhatever Auf einen Singleton zugreifen, und dokumentieren Sie dies, um die Singleton-Instanz in Ihrem Header abzurufen. Das sollte in den allermeisten Fällen alles sein, was Sie brauchen.

86
Chris Hanson

NS_UNAVAILABLE

- (instancetype)init NS_UNAVAILABLE;

Dies ist die Kurzversion des nicht verfügbaren Attributs. Es erschien zuerst in macOS 10.7 und iOS 5 . Es ist in NSObjCRuntime.h als #define NS_UNAVAILABLE UNAVAILABLE_ATTRIBUTE Definiert.

Es gibt eine Version, die deaktiviert die Methode nur für Swift clients , nicht für ObjC-Code:

- (instancetype)init NS_Swift_UNAVAILABLE;

unavailable

Fügen Sie dem Header das Attribut unavailable hinzu, um bei jedem Aufruf von init einen Compilerfehler zu generieren.

-(instancetype) init __attribute__((unavailable("init not available")));  

compile time error

Wenn Sie keinen Grund haben, geben Sie einfach __attribute__((unavailable)) oder sogar __unavailable Ein:

-(instancetype) __unavailable init;  

doesNotRecognizeSelector:

Verwenden Sie doesNotRecognizeSelector: , um eine NSInvalidArgumentException auszulösen. “Das Laufzeitsystem ruft diese Methode immer dann auf, wenn ein Objekt eine aSelector-Nachricht empfängt, auf die es nicht antworten oder die es nicht weiterleiten kann.”

- (instancetype) init {
    [self release];
    [super doesNotRecognizeSelector:_cmd];
    return nil;
}

NSAssert

Verwenden Sie NSAssert , um die NSInternalInconsistencyException auszulösen und eine Nachricht anzuzeigen:

- (instancetype) init {
    [self release];
    NSAssert(false,@"unavailable, use initWithBlah: instead");
    return nil;
}

raise:format:

Verwenden Sie raise:format: , um Ihre eigene Ausnahme auszulösen:

- (instancetype) init {
    [self release];
    [NSException raise:NSGenericException 
                format:@"Disabled. Use +[[%@ alloc] %@] instead",
                       NSStringFromClass([self class]),
                       NSStringFromSelector(@selector(initWithStateDictionary:))];
    return nil;
}

[self release] Wird benötigt, da das Objekt bereits allocated war. Bei Verwendung von ARC ruft der Compiler dies für Sie auf. In jedem Fall kein Grund zur Sorge, wenn Sie die Ausführung absichtlich beenden möchten.

objc_designated_initializer

Falls Sie init deaktivieren möchten, um die Verwendung eines bestimmten Initialisierers zu erzwingen, gibt es dafür ein Attribut:

-(instancetype)myOwnInit NS_DESIGNATED_INITIALIZER;

Dies erzeugt eine Warnung, sofern keine andere Initialisierungsmethode myOwnInit intern aufruft. Details werden in Übernahme von Modern Objective-C nach der nächsten Xcode-Veröffentlichung veröffentlicht (ich vermute).

332
Jano

Apple hat begonnen, die folgenden Elemente in den Header-Dateien zu verwenden, um den Init-Konstruktor zu deaktivieren:

- (instancetype)init NS_UNAVAILABLE;

Dies wird in Xcode korrekt als Compilerfehler angezeigt. Dies wird speziell in mehreren HealthKit-Headerdateien festgelegt (HKUnit ist eine davon).

100
lehn0058

Wenn Sie über die Standardmethode -init sprechen, ist dies nicht möglich. Es wurde von NSObject geerbt und jede Klasse antwortet darauf ohne Warnungen.

Sie können eine neue Methode erstellen, z. B. -initMyClass, und sie in eine private Kategorie einordnen, wie es Matt vorschlägt. Definieren Sie dann die Standardmethode -init, um entweder eine Ausnahme auszulösen, wenn sie aufgerufen wird, oder (besser) Ihre private Methode -initMyClass mit einigen Standardwerten aufzurufen.

Einer der Hauptgründe, warum Leute Init verstecken wollen, ist Singleton-Objekte . Wenn dies der Fall ist, müssen Sie -init nicht ausblenden, sondern geben stattdessen das Singleton-Objekt zurück (oder erstellen, falls es noch nicht vorhanden ist).

3

Fügen Sie dies in die Header-Datei ein

- (id)init UNAVAILABLE_ATTRIBUTE;
3
Jerry Juang

nun, das Problem, warum Sie es nicht "privat/unsichtbar" machen können, ist, dass die init-Methode an id gesendet wird (da alloc eine id zurückgibt) und nicht an YourClass

Beachten Sie, dass eine ID aus der Sicht des Compilers (Checkers) möglicherweise auf alles reagiert, was jemals eingegeben wurde (es kann nicht überprüft werden, was zur Laufzeit wirklich in die ID fließt), sodass Sie init nur dann ausblenden können, wenn nirgendwo anders wäre (public = in) header) benutze eine Methode init, wie die Kompilierung wissen würde, dass es für id keine Möglichkeit gibt, auf init zu antworten, da es nirgendwo ein Init gibt (in deiner Quelle, alle Bibliotheken etc ...)

sie können dem Benutzer also nicht verbieten, init zu übergeben und vom Compiler zerschlagen zu werden. Sie können jedoch verhindern, dass der Benutzer eine echte Instanz erhält, indem Sie init aufrufen

einfach durch das Implementieren von init, das nil zurückgibt und einen (privaten/unsichtbaren) Initialisierer hat, dessen Name jemand anderes nicht bekommt (wie 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;
}

Hinweis: das könnte jemand machen

SomeClass * c = [[SomeClass alloc] initOnce];

und es würde tatsächlich eine neue Instanz zurückgeben, aber wenn die InitOnce nirgendwo in unserem Projekt öffentlich (im Header) deklariert würde, würde sie eine Warnung erzeugen (ID könnte nicht antworten ...) und die Person, die diese verwendet, würde es sowieso brauchen um genau zu wissen, dass der eigentliche Initialisierer die InitOnce ist

wir könnten dies noch weiter verhindern, aber es gibt keine Notwendigkeit

2
Peter Lapisu

Das hängt davon ab, was Sie unter "privat machen" verstehen. In Objective-C kann das Aufrufen einer Methode für ein Objekt besser als das Senden einer Nachricht an dieses Objekt beschrieben werden. Es gibt nichts in der Sprache, was einen Client daran hindert, eine bestimmte Methode für ein Objekt aufzurufen. Das Beste, was Sie tun können, ist, die Methode nicht in der Header-Datei zu deklarieren. Wenn ein Client dennoch die "private" Methode mit der richtigen Signatur aufruft, wird sie zur Laufzeit ausgeführt.

Die gebräuchlichste Methode zum Erstellen einer privaten Methode in Objective-C ist das Erstellen eines Category in der Implementierungsdatei und das Deklarieren aller darin enthaltenen "versteckten" Methoden. Denken Sie daran, dass dies die Ausführung von Aufrufen von init nicht wirklich verhindert, der Compiler jedoch Warnungen ausgibt, wenn jemand dies versucht.

MyClass.m

@interface MyClass (PrivateMethods)
- (NSString*) init;
@end

@implementation MyClass

- (NSString*) init
{
    // code...
}

@end

Es gibt einen anständigen Thread auf MacRumors.com zu diesem Thema.

1
Matt Dillard

Sie können jede Methode mit NS_UNAVAILABLE Als nicht verfügbar deklarieren.

Sie können diese Zeilen also unter Ihrem @Interface platzieren

- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;

Definieren Sie noch besser ein Makro in Ihrem Präfix-Header

#define NO_INIT \
- (instancetype)init NS_UNAVAILABLE; \
+ (instancetype)new NS_UNAVAILABLE;

und

@interface YourClass : NSObject
NO_INIT

// Your properties and messages

@end
1
Kaunteya

Ich muss erwähnen, dass das Platzieren von Behauptungen und das Auslösen von Ausnahmen, um Methoden in der Unterklasse zu verbergen, eine üble Falle für die guten Vorsätze darstellt.

Ich würde empfehlen, __unavailable Als zu verwenden. Jano erklärte dies für sein erstes Beispiel .

Methoden können in Unterklassen überschrieben werden. Dies bedeutet, dass wenn eine Methode in der Oberklasse eine Methode verwendet, die nur eine Ausnahme in der Unterklasse auslöst, diese wahrscheinlich nicht wie beabsichtigt funktioniert. Mit anderen Worten, Sie haben gerade gebrochen, was früher funktioniert hat. Dies gilt auch für Initialisierungsmethoden. Hier ist ein Beispiel für eine solche häufig vorkommende Implementierung:

- (SuperClass *)initWithParameters:(Type1 *)arg1 optional:(Type2 *)arg2
{
    ...bla bla...
    return self;
}

- (SuperClass *)initWithLessParameters:(Type1 *)arg1
{
    self = [self initWithParameters:arg1 optional:DEFAULT_ARG2];
    return self;
}

Stellen Sie sich vor, was mit -initWithLessParameters passiert, wenn ich dies in der Unterklasse mache:

- (SubClass *)initWithParameters:(Type1 *)arg1 optional:(Type2 *)arg2
{
    [self release];
    [super doesNotRecognizeSelector:_cmd];
    return nil;
}

Dies impliziert, dass Sie private (versteckte) Methoden verwenden sollten, insbesondere bei Initialisierungsmethoden, sofern Sie nicht vorhaben, die Methoden außer Kraft zu setzen. Dies ist jedoch ein anderes Thema, da Sie bei der Implementierung der Superklasse nicht immer die volle Kontrolle haben. (Dies lässt mich die Verwendung von __attribute ((objc_designated_initializer)) als schlechte Praxis in Frage stellen, obwohl ich sie nicht ausführlich verwendet habe.)

Dies bedeutet auch, dass Sie Zusicherungen und Ausnahmen in Methoden verwenden können, die in Unterklassen überschrieben werden müssen. (Die "abstrakten" Methoden wie in Erstellen einer abstrakten Klasse in Objective-C )

Und vergessen Sie nicht die + neue Klassenmethode.

0
techniao