Ich muss die -init
Methode meiner Klasse in Objective-C ausblenden (privat machen) .
Wie kann ich das machen?
objective-c
lajos
quelle
quelle
NS_UNAVAILABLE
. Ich möchte Sie generell dringend bitten, diesen Ansatz zu verwenden. Würde das OP erwägen, seine akzeptierte Antwort zu überarbeiten? Die anderen Antworten hier liefern viele nützliche Details, sind jedoch nicht die bevorzugte Methode, um dies zu erreichen.NS_UNAVAILABLE
kann ein Anrufer weiterhininit
indirekt über aufrufennew
. Durch einfaches Überschreibeninit
der Rückgabenil
werden beide Fälle behandelt.Antworten:
Objective-C hat wie Smalltalk kein Konzept für "private" oder "öffentliche" Methoden. Jede Nachricht kann jederzeit an ein beliebiges Objekt gesendet werden.
Was Sie tun können, ist eine zu werfen
NSInternalInconsistencyException
wenn Ihre-init
Methode aufgerufen wird:Die andere Alternative - die in der Praxis wahrscheinlich weitaus besser ist - besteht darin,
-init
wenn möglich etwas Sinnvolles für Ihre Klasse zu tun.Wenn Sie dies versuchen, weil Sie sicherstellen möchten, dass ein Singleton-Objekt verwendet wird, stören Sie sich nicht. Genauer gesagt, nicht mit der Mühe „Aufschalten
+allocWithZone:
,-init
,-retain
,-release
“ Methode der singletons erzeugen. Es ist praktisch immer unnötig und fügt nur Komplikationen hinzu, ohne wirklich einen signifikanten Vorteil zu erzielen.Schreiben Sie stattdessen einfach Ihren Code so, dass Sie mit Ihrer
+sharedWhatever
Methode 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.quelle
alloc
undinit
und ihre Code - Funktion hat falsch , weil sie die richtige Klasse haben, aber die falsche Instanz. Dies ist die Essenz des Prinzips der Einkapselung in OO. Sie verstecken Dinge in Ihren APIs, auf die andere Klassen keinen Zugriff benötigen oder erhalten sollten. Sie halten nicht nur alles öffentlich und erwarten von den Menschen, dass sie alles im Auge behalten.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 definiert
#define NS_UNAVAILABLE UNAVAILABLE_ATTRIBUTE
.Es gibt eine Version, die die Methode nur für Swift-Clients deaktiviert , nicht für ObjC-Code:
unavailable
Fügen Sie das
unavailable
Attribut dem Header hinzu, um bei jedem Aufruf von init einen Compilerfehler zu generieren .Wenn Sie keinen Grund haben, geben Sie einfach ein
__attribute__((unavailable))
oder sogar__unavailable
:doesNotRecognizeSelector:
Verwenden Sie
doesNotRecognizeSelector:
diese Option, 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 er nicht weiterleiten kann."NSAssert
Verwenden Sie
NSAssert
diese Option, um NSInternalInconsistencyException auszulösen und eine Nachricht anzuzeigen:raise:format:
Verwenden Sie
raise:format:
diese Option, um Ihre eigene Ausnahme auszulösen:[self release]
wird benötigt, weil das Objekt bereitsalloc
bearbeitet wurde. Wenn Sie ARC verwenden, ruft der Compiler es für Sie auf. Auf jeden Fall kein Grund zur Sorge, wenn Sie die Ausführung absichtlich beenden möchten.objc_designated_initializer
Falls Sie
init
die Verwendung eines bestimmten Initialisierers deaktivieren möchten, gibt es dafür ein Attribut:Dies generiert eine Warnung, es sei denn, eine andere Initialisierungsmethode ruft
myOwnInit
intern auf. Details werden nach der nächsten Xcode-Veröffentlichung in Adopting Modern Objective-C veröffentlicht (denke ich).quelle
init
. Wenn diese Methode ungültig ist, warum initialisieren Sie dann ein Objekt? Wenn Sie eine Ausnahme auslösen, können Sie außerdem eine benutzerdefinierte Nachricht angebeninit*
, die dem Entwickler die richtige Methode mitteilt , während Sie im Falle von keine solche Option habendoesNotRecognizeSelector
.Apple hat damit begonnen, Folgendes in seinen Header-Dateien zu verwenden, um den Init-Konstruktor zu deaktivieren:
Dies wird in Xcode korrekt als Compilerfehler angezeigt. Dies wird insbesondere in mehreren ihrer HealthKit-Headerdateien festgelegt (HKUnit ist eine davon).
quelle
Wenn Sie über die Standardmethode -init sprechen, können Sie dies nicht. Es wurde von NSObject geerbt und jede Klasse wird ohne Warnungen darauf antworten.
Sie können eine neue Methode erstellen, z. B. -initMyClass, und sie in eine private Kategorie einordnen, wie von Matt vorgeschlagen. Definieren Sie dann die Standardmethode -init, um entweder eine Ausnahme auszulösen, wenn sie aufgerufen wird, oder (besser) Ihre private -initMyClass mit einigen Standardwerten aufzurufen.
Einer der Hauptgründe, warum Leute init verstecken wollen, sind Singleton-Objekte . Wenn dies der Fall ist, müssen Sie -init nicht ausblenden. Geben Sie stattdessen einfach das Singleton-Objekt zurück (oder erstellen Sie es, wenn es noch nicht vorhanden ist).
quelle
Fügen Sie dies in die Header-Datei ein
quelle
Sie können jede Methode als nicht verfügbar deklarieren
NS_UNAVAILABLE
.Sie können diese Zeilen also unter Ihre @ Schnittstelle setzen
Definieren Sie noch besser ein Makro in Ihrem Präfix-Header
und
quelle
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 weiterhin ausgeführt.
Die häufigste Methode zum Erstellen einer privaten Methode in Objective-C besteht darin, eine Kategorie in der Implementierungsdatei zu erstellen und alle darin enthaltenen "versteckten" Methoden zu deklarieren. Denken Sie daran, dass dies nicht wirklich verhindert, dass Aufrufe
init
ausgeführt werden, aber der Compiler gibt Warnungen aus, wenn jemand dies versucht.MyClass.m
Auf MacRumors.com gibt es einen anständigen Thread zu diesem Thema.
quelle
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 vom Standpunkt des Compilers (Checker) aus eine ID möglicherweise auf alles reagieren kann, was jemals eingegeben wurde (sie kann nicht überprüfen, was zur Laufzeit wirklich in die ID eingeht), sodass Sie init nur dann ausblenden können, wenn nichts nirgendwo ist (öffentlich = in) Header) Verwenden Sie eine Methode init, als die Kompilierung wissen würde, dass es keine Möglichkeit gibt, dass id auf init reagiert, da es nirgendwo init gibt (in Ihrer Quelle, in allen Bibliotheken usw.)
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 durch Aufrufen eines init eine echte Instanz erhält
einfach durch die Implementierung von init, das nil zurückgibt und einen (privaten / unsichtbaren) Initialisierer hat, dessen Name jemand anderes nicht erhält (wie initOnce, initWithSpecial ...)
Hinweis: dass jemand dies tun könnte
und es würde tatsächlich eine neue Instanz zurückgeben, aber wenn das initOnce nirgends in unserem Projekt öffentlich (im Header) deklariert würde, würde es eine Warnung erzeugen (ID könnte nicht antworten ...) und die Person, die dies verwendet, würde es sowieso brauchen um genau zu wissen, dass der eigentliche Initialisierer der initOnce ist
wir könnten dies noch weiter verhindern, aber es besteht keine Notwendigkeit
quelle
Ich muss erwähnen, dass das Platzieren von Behauptungen und das Auslösen von Ausnahmen zum Ausblenden von Methoden in der Unterklasse eine böse Falle für die gut gemeinten Personen darstellt.
Ich würde empfehlen,
__unavailable
wie Jano für sein erstes Beispiel erklärt hat .Methoden können in Unterklassen überschrieben werden. Dies bedeutet, dass eine Methode in der Oberklasse, die eine Methode verwendet, die nur eine Ausnahme in der Unterklasse auslöst, 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 ziemlich häufige Implementierung:
Stellen Sie sich vor, was mit -initWithLessParameters passiert, wenn ich dies in der Unterklasse mache:
Dies bedeutet, dass Sie dazu neigen sollten, private (versteckte) Methoden zu verwenden, insbesondere bei Initialisierungsmethoden, es sei denn, Sie planen, die Methoden überschreiben zu lassen. Dies ist jedoch ein anderes Thema, da Sie bei der Implementierung der Oberklasse 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 es 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 beim Erstellen einer abstrakten Klasse in Objective-C )
Und vergessen Sie nicht die + neue Klassenmethode.
quelle