J'ai besoin de cacher (rendre privée) la -init
méthode de ma classe en Objective-C.
Comment puis je faire ça?
objective-c
lajos
la source
la source
NS_UNAVAILABLE
. Je vous exhorte généralement à utiliser cette approche. Le PO envisagerait-il de réviser sa réponse acceptée? Les autres réponses ici fournissent beaucoup de détails utiles, mais ne sont pas la méthode préférée pour y parvenir.NS_UNAVAILABLE
permet toujours à un appelant d'appelerinit
indirectement vianew
. Le simple fait de remplacerinit
par returnnil
traitera les deux cas.Réponses:
Objective-C, comme Smalltalk, n'a aucun concept de méthodes «privées» ou «publiques». Tout message peut être envoyé à n'importe quel objet à tout moment.
Ce que vous pouvez faire est de lancer un
NSInternalInconsistencyException
si votre-init
méthode est appelée:L'autre alternative - qui est probablement bien meilleure en pratique - est de faire
-init
quelque chose de sensé pour votre classe si possible.Si vous essayez de faire cela parce que vous essayez de "vous assurer" qu'un objet singleton est utilisé, ne vous inquiétez pas. Plus précisément, ne vous embêtez pas avec la « dérogation
+allocWithZone:
,-init
,-retain
,-release
» méthode de création singletons. C'est pratiquement toujours inutile et ne fait qu'ajouter des complications sans véritable avantage significatif.Au lieu de cela, écrivez simplement votre code de manière à ce que votre
+sharedWhatever
méthode soit la façon dont vous accédez à un singleton, et documentez-le comme le moyen d'obtenir l'instance de singleton dans votre en-tête. Cela devrait être tout ce dont vous avez besoin dans la grande majorité des cas.la source
alloc
etinit
et ont leur fonction de code mal parce qu'ils ont la bonne classe, mais le mauvais exemple. C'est l'essence du principe de l' encapsulation en OO. Vous cachez dans vos API des éléments auxquels les autres classes ne devraient pas avoir besoin, ou auxquelles ils n'ont pas accès. Vous ne gardez pas seulement tout public et vous attendez des humains à garder une trace de tout cela.NS_UNAVAILABLE
Il s'agit d'une version courte de l'attribut indisponible. Il est apparu pour la première fois dans macOS 10.7 et iOS 5 . Il est défini dans NSObjCRuntime.h comme
#define NS_UNAVAILABLE UNAVAILABLE_ATTRIBUTE
.Il existe une version qui désactive la méthode uniquement pour les clients Swift , pas pour le code ObjC:
unavailable
Ajoutez l'
unavailable
attribut à l'en-tête pour générer une erreur du compilateur sur tout appel à init.Si vous n'avez pas de raison, tapez simplement
__attribute__((unavailable))
, ou même__unavailable
:doesNotRecognizeSelector:
Utilisez
doesNotRecognizeSelector:
pour déclencher une NSInvalidArgumentException. "Le système d'exécution appelle cette méthode chaque fois qu'un objet reçoit un message aSelector auquel il ne peut pas répondre ou le transférer."NSAssert
Utilisez
NSAssert
pour lancer NSInternalInconsistencyException et afficher un message:raise:format:
Utilisez
raise:format:
pour lancer votre propre exception:[self release]
est nécessaire car l'objet a déjà étéalloc
affecté. Lors de l'utilisation d'ARC, le compilateur l'appellera pour vous. Dans tous les cas, pas de quoi s'inquiéter lorsque vous êtes sur le point d'arrêter intentionnellement l'exécution.objc_designated_initializer
Dans le cas où vous avez l'intention de désactiver
init
pour forcer l'utilisation d'un initialiseur désigné, il existe un attribut pour cela:Cela génère un avertissement à moins qu'une autre méthode d'initialisation n'appelle en
myOwnInit
interne. Les détails seront publiés dans Adopting Modern Objective-C après la prochaine version de Xcode (je suppose).la source
init
. Depuis, si cette méthode n'est pas valide, pourquoi initialisez-vous un objet? De plus, lors de la levée d'une exception, vous pourrez spécifier un message personnalisé communiquant la bonneinit*
méthode au développeur, alors que vous n'avez pas cette option dans le cas dedoesNotRecognizeSelector
.Apple a commencé à utiliser ce qui suit dans ses fichiers d'en-tête pour désactiver le constructeur init:
Cela s'affiche correctement comme une erreur de compilation dans Xcode. Plus précisément, cela est défini dans plusieurs de leurs fichiers d'en-tête HealthKit (HKUnit en fait partie).
la source
Si vous parlez de la méthode par défaut -init, vous ne pouvez pas. Il est hérité de NSObject et chaque classe y répondra sans avertissement.
Vous pouvez créer une nouvelle méthode, par exemple -initMyClass, et la mettre dans une catégorie privée comme le suggère Matt. Définissez ensuite la méthode par défaut -init pour lever une exception si elle est appelée ou (mieux) appeler votre -initMyClass privé avec des valeurs par défaut.
L'une des principales raisons pour lesquelles les gens semblent vouloir cacher init est pour les objets singleton . Si tel est le cas, vous n'avez pas besoin de masquer -init, retournez simplement l'objet singleton à la place (ou créez-le s'il n'existe pas encore).
la source
Mettez ceci dans le fichier d'en-tête
la source
Vous pouvez déclarer toute méthode indisponible en utilisant
NS_UNAVAILABLE
.Vous pouvez donc mettre ces lignes sous votre @interface
Encore mieux définir une macro dans votre en-tête de préfixe
et
la source
Cela dépend de ce que vous entendez par «rendre privé». En Objective-C, l'appel d'une méthode sur un objet peut être mieux décrit comme l'envoi d'un message à cet objet. Il n'y a rien dans le langage qui empêche un client d'appeler une méthode donnée sur un objet; le mieux que vous puissiez faire est de ne pas déclarer la méthode dans le fichier d'en-tête. Si un client appelle néanmoins la méthode "privée" avec la bonne signature, elle s'exécutera toujours à l'exécution.
Cela dit, la manière la plus courante de créer une méthode privée en Objective-C est de créer une catégorie dans le fichier d'implémentation et de déclarer toutes les méthodes "cachées" qui s'y trouvent. Souvenez-vous que cela n'empêchera pas vraiment l'
init
exécution des appels à , mais le compilateur crachera des avertissements si quelqu'un essaie de le faire.MyClass.m
Il y a un fil décent sur MacRumors.com à ce sujet.
la source
Eh bien, le problème pour lequel vous ne pouvez pas le rendre "privé / invisible" est que la méthode init est envoyée à id (car alloc renvoie un id) et non à YourClass
Notez qu'à partir du point du compilateur (vérificateur), un identifiant pourrait potentiellement répondre à tout ce qui a été tapé (il ne peut pas vérifier ce qui entre vraiment dans l'identifiant au moment de l'exécution), donc vous ne pouvez masquer init que lorsque rien ne le ferait nulle part (publiquement = in header) utilisez une méthode init, que la compilation sait, qu'il n'y a aucun moyen pour id de répondre à init, car il n'y a aucun init nulle part (dans votre source, toutes les libs etc ...)
donc vous ne pouvez pas interdire à l'utilisateur de passer init et d'être écrasé par le compilateur ... mais ce que vous pouvez faire, c'est empêcher l'utilisateur d'obtenir une instance réelle en appelant un init
simplement en implémentant init, qui renvoie nil et dispose d'un initialiseur (privé / invisible) dont le nom que quelqu'un d'autre ne recevra pas (comme initOnce, initWithSpecial ...)
Remarque: que quelqu'un pourrait le faire
et il renverrait en fait une nouvelle instance, mais si l'initOnce n'était nulle part dans notre projet déclaré publiquement (dans l'en-tête), il générerait un avertissement (id pourrait ne pas répondre ...) et de toute façon la personne qui l'utilise aurait besoin pour savoir exactement que le vrai initialiseur est l'initOnce
nous pourrions empêcher cela encore plus, mais il n'est pas nécessaire
la source
Je dois mentionner que placer des assertions et lever des exceptions pour masquer des méthodes dans la sous-classe a un piège méchant pour les bien intentionnés.
Je recommanderais d'utiliser
__unavailable
comme Jano l'a expliqué pour son premier exemple .Les méthodes peuvent être remplacées dans les sous-classes. Cela signifie que si une méthode de la superclasse utilise une méthode qui lève simplement une exception dans la sous-classe, elle ne fonctionnera probablement pas comme prévu. En d'autres termes, vous venez de casser ce qui fonctionnait auparavant. Cela est également vrai avec les méthodes d'initialisation. Voici un exemple d'une telle implémentation assez courante:
Imaginez ce qui arrive à -initWithLessParameters, si je fais cela dans la sous-classe:
Cela implique que vous devriez avoir tendance à utiliser des méthodes privées (masquées), en particulier dans les méthodes d'initialisation, à moins que vous ne prévoyiez de remplacer les méthodes. Mais c'est un autre sujet, car vous n'avez pas toujours un contrôle total sur l'implémentation de la superclasse. (Cela me fait remettre en question l'utilisation de __attribute ((objc_designated_initializer)) comme une mauvaise pratique, bien que je ne l'ai pas utilisé en profondeur.)
Cela implique également que vous pouvez utiliser des assertions et des exceptions dans les méthodes qui doivent être remplacées dans les sous-classes. (Les méthodes "abstraites" comme dans Créer une classe abstraite en Objective-C )
Et n'oubliez pas la + nouvelle méthode de classe.
la source