Je suis à l'origine un programmeur Java qui travaille maintenant avec Objective-C. Je voudrais créer une classe abstraite, mais cela ne semble pas possible dans Objective-C. Est-ce possible?
Sinon, à quelle distance d'une classe abstraite puis-je obtenir en Objective-C?
objective-c
abstract-class
Jonathan Arbogast
la source
la source
Réponses:
En règle générale, les classes Objective-C sont abstraites par convention uniquement - si l'auteur documente une classe comme abstraite, ne l'utilisez pas sans la sous-classer. Cependant, il n'y a pas d'application au moment de la compilation qui empêche l'instanciation d'une classe abstraite. En fait, rien n'empêche un utilisateur de fournir des implémentations de méthodes abstraites via une catégorie (c'est-à-dire au moment de l'exécution). Vous pouvez forcer un utilisateur à remplacer au moins certaines méthodes en levant une exception dans l'implémentation de ces méthodes dans votre classe abstraite:
Si votre méthode retourne une valeur, c'est un peu plus facile à utiliser
comme alors vous n'avez pas besoin d'ajouter une déclaration de retour de la méthode.
Si la classe abstraite est vraiment une interface (c'est-à-dire qu'elle n'a pas d'implémentation de méthode concrète), l'utilisation d'un protocole Objective-C est l'option la plus appropriée.
la source
@protocol
définition, mais vous ne pouvez pas y définir les méthodes.+ (instancetype)exceptionForCallingAbstractMethod:(SEL)selector
cela fonctionne très bien.doesNotRecognizeSelector
.Non, il n'y a aucun moyen de créer une classe abstraite dans Objective-C.
Vous pouvez vous moquer d'une classe abstraite - en faisant appeler les méthodes / sélecteurs doesNotRecognizeSelector: et donc lever une exception rendant la classe inutilisable.
Par exemple:
Vous pouvez également le faire pour init.
la source
NSObject
référence suggère d'utiliser ceci où vous ne voulez PAS hériter d'une méthode, pas pour forcer le remplacement d'une méthode. Bien que cela puisse être la même chose, peut-être :)Juste en riffant sur la réponse de @Barry Wark ci-dessus (et en mettant à jour pour iOS 4.3) et en laissant cela pour ma propre référence:
puis dans vos méthodes, vous pouvez utiliser cette
Remarques: Je ne sais pas si faire une macro ressemble à une fonction C est une bonne idée ou non, mais je vais le garder jusqu'à ce que vous appreniez le contraire. Je pense qu'il est plus correct d'utiliser
NSInvalidArgumentException
(plutôt queNSInternalInconsistencyException
) car c'est ce que le système d'exécution lance en réponse à l'doesNotRecognizeSelector
appel (voir laNSObject
documentation).la source
universe.thing
mais il se développe[Universe universe].thing
. Grand plaisir, sauver des milliers de lettres de code ...#define mustOverride() @throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"%s must be overridden in a subclass/category", __PRETTY_FUNCTION__] userInfo:nil]
.__PRETTY_FUNCTION__
partout, via une macro DLog (...), comme suggéré ici: stackoverflow.com/a/969291/38557 .#define setMustOverride() NSLog(@"%@ - method not implemented", NSStringFromClass([self class])); mustOverride()
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[BaseDynamicUIViewController localizeUI] must be overridden in a subclass/category'
dans le cas où je propose, vous obtiendrez égalementHomeViewController - method not implemented
où HomeViewController hérité de Base - ce donnera plus d'informationsLa solution que j'ai trouvée est:
De cette façon, le compilateur vous donnera un avertissement pour toute méthode du protocole qui n'est pas implémentée par votre classe enfant.
Ce n'est pas aussi succinct qu'en Java, mais vous obtenez l'avertissement du compilateur souhaité.
la source
NSObject
). Si vous placez ensuite le protocole et la déclaration de classe de base dans le même fichier d'en-tête, il est presque impossible de les distinguer d'une classe abstraite.À partir de la liste de diffusion Omni Group :
Objective-C n'a pas la construction du compilateur abstrait comme Java pour le moment.
Donc, tout ce que vous faites est de définir la classe abstraite comme toute autre classe normale et d'implémenter des talons de méthodes pour les méthodes abstraites qui sont vides ou signalent la non-prise en charge du sélecteur. Par exemple...
Je fais également ce qui suit pour empêcher l'initialisation de la classe abstraite via l'initialiseur par défaut.
la source
Au lieu d'essayer de créer une classe de base abstraite, envisagez d'utiliser un protocole (similaire à une interface Java). Cela vous permet de définir un ensemble de méthodes, puis d'accepter tous les objets conformes au protocole et d'implémenter les méthodes. Par exemple, je peux définir un protocole d'opération, puis avoir une fonction comme celle-ci:
Où op peut être n'importe quel objet implémentant le protocole d'opération.
Si vous avez besoin de votre classe de base abstraite pour faire plus que simplement définir des méthodes, vous pouvez créer une classe Objective-C régulière et empêcher qu'elle ne soit instanciée. Remplacez simplement la fonction - (id) init et faites-la retourner nil ou assert (false). Ce n'est pas une solution très propre, mais comme Objective-C est entièrement dynamique, il n'y a vraiment pas d'équivalent direct à une classe de base abstraite.
la source
Ce fil est un peu ancien, et la plupart de ce que je veux partager est déjà là.
Cependant, ma méthode préférée n'est pas mentionnée, et AFAIK il n'y a pas de support natif dans le Clang actuel, alors c'est parti…
Tout d'abord, et surtout (comme d'autres l'ont déjà souligné), les classes abstraites sont quelque chose de très rare dans Objective-C - nous utilisons généralement la composition (parfois par délégation) à la place. C'est probablement la raison pour laquelle une telle fonctionnalité n'existe pas déjà dans le langage / compilateur - à part les
@dynamic
propriétés, que l'IIRC a ajoutées dans ObjC 2.0 accompagnant l'introduction de CoreData.Mais étant donné que (après une évaluation minutieuse de votre situation!) Vous êtes arrivé à la conclusion que la délégation (ou la composition en général) n'est pas bien adaptée pour résoudre votre problème, voici comment je le fais:
[self doesNotRecognizeSelector:_cmd];
…__builtin_unreachable();
pour faire taire l'avertissement que vous obtiendrez pour les méthodes non nulles, vous indiquant «le contrôle a atteint la fin de la fonction non nulle sans retour».-[NSObject doesNotRecognizeSelector:]
utilisation__attribute__((__noreturn__))
dans une catégorie sans implémentation afin de ne pas remplacer l'implémentation d'origine de cette méthode, et inclure l'en-tête de cette catégorie dans le PCH de votre projet.Personnellement, je préfère la version macro car cela me permet de réduire le plus possible le passe-partout.
C'est ici:
Comme vous pouvez le voir, la macro fournit l'implémentation complète des méthodes abstraites, réduisant la quantité nécessaire de passe-partout au minimum absolu.
Une option encore meilleure serait de faire pression sur l' équipe Clang pour fournir un attribut de compilateur pour ce cas, via des demandes de fonctionnalités. (Mieux, car cela permettrait également des diagnostics au moment de la compilation pour les scénarios où vous sous-classe, par exemple NSIncrementalStore.)
Pourquoi je choisis cette méthode
__builtin_unreachable()
peut surprendre les gens, mais c'est assez facile à comprendre aussi.)Ce dernier point nécessite quelques explications, je suppose:
Certaines personnes (la plupart?) Suppriment les assertions dans les versions. (Je ne suis pas d'accord avec cette habitude, mais c'est une autre histoire…) Le fait de ne pas implémenter une méthode requise - cependant - est mauvais , terrible , mauvais et, fondamentalement, la fin de l'univers pour votre programme. Votre programme ne peut pas fonctionner correctement à cet égard car il n'est pas défini et un comportement indéfini est la pire chose qui soit. Par conséquent, être en mesure de supprimer ces diagnostics sans générer de nouveaux diagnostics serait totalement inacceptable.
C'est déjà assez grave pour que vous ne puissiez pas obtenir de diagnostics de compilation corrects pour ces erreurs de programmation, et que vous ayez à recourir à la découverte au moment de l'exécution pour celles-ci, mais si vous pouvez les recouvrir dans les versions, pourquoi essayer d'avoir une classe abstraite dans le première place?
la source
__builtin_unreachable();
bijou fait que cela fonctionne parfaitement. La macro la rend auto-documentée et le comportement correspond à ce qui se passe si vous appelez une méthode manquante sur un objet.Utilisation
@property
et@dynamic
pourrait également fonctionner. Si vous déclarez une propriété dynamique et ne donnez pas d'implémentation de méthode correspondante, tout sera compilé sans avertissements et vous obtiendrez uneunrecognized selector
erreur au moment de l'exécution si vous essayez d'y accéder. C'est essentiellement la même chose que d'appeler[self doesNotRecognizeSelector:_cmd]
, mais avec beaucoup moins de frappe.la source
Dans Xcode (en utilisant clang, etc.), j'aime utiliser
__attribute__((unavailable(...)))
pour baliser les classes abstraites afin que vous obteniez une erreur / un avertissement si vous essayez de l'utiliser.Il offre une certaine protection contre l'utilisation accidentelle de la méthode.
Exemple
Dans la
@interface
balise de classe de base, les méthodes "abstraites":En allant plus loin, je crée une macro:
Cela vous permet de faire ceci:
Comme je l'ai dit, ce n'est pas une véritable protection du compilateur, mais c'est à peu près aussi efficace que vous allez obtenir dans un langage qui ne prend pas en charge les méthodes abstraites.
la source
-init
méthode dans votre sous-classe.La réponse à la question est dispersée dans les commentaires sous les réponses déjà données. Donc, je résume et simplifie simplement ici.
Option 1: protocoles
Si vous souhaitez créer une classe abstraite sans implémentation, utilisez «Protocoles». Les classes héritant d'un protocole sont obligées d'implémenter les méthodes dans le protocole.
Option2: modèle de méthode de modèle
Si vous voulez créer une classe abstraite avec une implémentation partielle comme "Pattern Method Pattern", alors c'est la solution. Objective-C - Modèle de méthodes de modèle?
la source
Une autre alternative
Il vous suffit de vérifier la classe dans la classe Abstract et d'affirmer ou d'exclure ce que vous voulez.
Cela supprime la nécessité de passer outre
init
la source
(plus d'une suggestion connexe)
Je voulais avoir un moyen de faire savoir au programmeur "ne pas appeler de l'enfant" et de passer outre complètement (dans mon cas, toujours offrir certaines fonctionnalités par défaut au nom du parent lorsqu'il n'est pas étendu):
L'avantage est que le programmeur VOIRa le "remplacement" dans la déclaration et saura qu'il ne doit pas appeler
[super ..]
.Certes, il est moche d'avoir à définir des types de retour individuels pour cela, mais cela sert de bon indice visuel et vous ne pouvez pas facilement utiliser la partie "override_" dans une définition de sous-classe.
Bien sûr, une classe peut toujours avoir une implémentation par défaut lorsqu'une extension est facultative. Mais comme le disent les autres réponses, implémentez une exception d'exécution le cas échéant, comme pour les classes abstraites (virtuelles).
Ce serait bien d'avoir intégré des astuces de compilateur comme celle-ci, même des astuces pour quand il est préférable de pré / post appeler l'implémentation du super, au lieu d'avoir à fouiller dans les commentaires / documentation ou ... à supposer.
la source
Si vous êtes habitué à ce que le compilateur détecte des violations d'instanciation abstraites dans d'autres langues, le comportement d'Objective-C est décevant.
En tant que langage de liaison tardive, il est clair qu'Objective-C ne peut pas décider statiquement si une classe est vraiment abstraite ou non (vous pourriez ajouter des fonctions au moment de l'exécution ...), mais pour les cas d'utilisation typiques, cela semble être un défaut. Je préférerais que le compilateur empêche les instanciations des classes abstraites au lieu de générer une erreur lors de l'exécution.
Voici un modèle que nous utilisons pour obtenir ce type de vérification statique en utilisant quelques techniques pour masquer les initialiseurs:
la source
Ce genre de situations ne devrait probablement se produire qu'au moment du développement, donc cela pourrait fonctionner:
la source
Vous pouvez utiliser une méthode proposée par @Yar (avec quelques modifications):
Ici, vous obtiendrez un message comme:
Ou affirmation:
Dans ce cas, vous obtiendrez:
Vous pouvez également utiliser des protocoles et d'autres solutions - mais c'est l'une des plus simples.
la source
Le cacao ne fournit rien appelé abstrait. Nous pouvons créer un résumé de classe qui n'est vérifié qu'au moment de l'exécution, et au moment de la compilation, ce n'est pas vérifié.
la source
Je désactive généralement la méthode init dans une classe que je souhaite abstraire:
Cela générera une erreur au moment de la compilation chaque fois que vous appelez init sur cette classe. J'utilise ensuite des méthodes de classe pour tout le reste.
Objective-C n'a aucun moyen intégré pour déclarer des classes abstraites.
la source
En modifiant un peu ce que @redfood a suggéré en appliquant le commentaire de @ dotToString, vous avez en fait la solution adoptée par IGListKit d'Instagram .
AbstractClass
doit être entré ou sorti par une méthode, saisissez-le à laAbstractClass<Protocol>
place.Parce que
AbstractClass
n'implémente pasProtocol
, la seule façon d'avoir uneAbstractClass<Protocol>
instance est de sous-classer. CommeAbstractClass
seul ne peut être utilisé nulle part dans le projet, il devient abstrait.Bien sûr, cela n'empêche pas les développeurs déconseillés d'ajouter de nouvelles méthodes se référant simplement à
AbstractClass
, ce qui finirait par autoriser une instance de la classe abstraite (plus maintenant).Exemple réel: IGListKit a une classe de base
IGListSectionController
qui n'implémente pas le protocoleIGListSectionType
, cependant chaque méthode qui nécessite une instance de cette classe, demande en fait le typeIGListSectionController<IGListSectionType>
. Par conséquent, il n'y a aucun moyen d'utiliser un objet de typeIGListSectionController
pour quelque chose d'utile dans leur cadre.la source
En fait, Objective-C n'a pas de classes abstraites, mais vous pouvez utiliser des protocoles pour obtenir le même effet. Voici l'exemple:
CustomProtocol.h
TestProtocol.h
TestProtocol.m
la source
Un exemple simple de création d'une classe abstraite
la source
Vous ne pouvez pas simplement créer un délégué?
Un délégué est comme une classe de base abstraite dans le sens où vous dites quelles fonctions doivent être définies, mais vous ne les définissez pas réellement.
Ensuite, chaque fois que vous implémentez votre délégué (c'est-à-dire la classe abstraite), le compilateur vous avertit des fonctions facultatives et obligatoires pour lesquelles vous devez définir le comportement.
Cela ressemble à une classe de base abstraite pour moi.
la source