151 votes

Est-il possible de rendre la méthode -init privée dans Objective-C?

Je dois masquer (rendre privé) la méthode -init de ma classe dans Objective-C.

Comment puis je faire ça?

357voto

Jano Points 37593

unavailable

Ajouter l' unavailable d'attribut à l'en-tête de générer une erreur de compilation sur tout appel à init.

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

compile time error

Si vous n'avez pas de raison, il suffit de taper __attribute__((unavailable)), ou même __unavailable:

-(instancetype) __unavailable init;  

doesNotRecognizeSelector:

Utiliser doesNotRecognizeSelector: d'élever un NSInvalidArgumentException. "Le système d'exécution appelle cette méthode chaque fois qu'un objet reçoit un aSelector message qu'il ne peut pas répondre ou transférer."

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

NSAssert

Utiliser NSAssert de jeter NSInternalInconsistencyException et affiche un message:

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

raise:format:

Utiliser raise:format: de lancer votre propre exception:

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

[self release] est nécessaire parce que l'objet était déjà alloc- indiqué. Lors de l'utilisation de l'ARC, le compilateur vous appeler. En tout cas, pas quelque chose à se soucier lorsque vous êtes sur le point de faire intentionnellement arrêter l'exécution.

objc_designated_initializer

Dans le cas où vous avez l'intention de désactiver init pour forcer l'utilisation d'un désignée d'initialiseur, il y a un attribut pour que:

-(instancetype)myOwnInit NS_DESIGNATED_INITIALIZER;

Cela génère un avertissement, à moins que tout autre initialiseur les appels de méthode myOwnInit en interne. Les détails seront publiés dans l'Adoption Moderne Objective-C , après la prochaine Xcode version (je suppose).

88voto

Chris Hanson Points 34485

Objective-C, comme Smalltalk, n'a pas de notion de "privé" et "public". Tout message peut être envoyé à n'importe quel objet à tout moment.

Ce que vous pouvez faire est de jeter un NSInternalInconsistencyException si vos -init méthode est appelée:

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

L'autre alternative, ce qui est probablement bien mieux dans la pratique - est de s' -init faire quelque chose d'intelligent pour votre classe si possible.

Si vous essayez de le faire parce que vous êtes à essayer de "s'assurer" un objet singleton est utilisé, ne vous embêtez pas. Plus précisément, ne vous embêtez pas avec le champ "remplacer +allocWithZone:, -init, -retain, -release" méthode de création des singletons. Il est presque toujours inutile et est juste que l'ajout de complications pour pas de réel avantage significatif.

Au lieu de cela, il suffit d'écrire votre code tel que votre +sharedWhatever méthode est la manière dont vous accédez à un singleton, et le document que comme le moyen d'obtenir l'instance du singleton dans votre en-tête. Cela devrait être tout ce dont vous avez besoin dans la grande majorité des cas.

3voto

Nathan Kinsinger Points 6202

Si vous parlez de la valeur par défaut-méthode init, alors vous ne pouvez pas. Il est hérité de NSObject et chaque classe de se répondre à elle avec aucun avertissement.

Vous pouvez créer une nouvelle méthode, disons -initMyClass, et les mettre dans une catégorie comme Matt suggère. Ensuite, de définir la valeur par défaut-init méthode pour lever une exception si elle ou (mieux), appelez votre privé initMyClass avec certaines valeurs par défaut.

L'une des principales raisons pour lesquelles les gens semblent vouloir cacher init est pour les objets singleton. Si c'est le cas, alors vous n'avez pas besoin de cacher -init, il suffit de retourner l'objet singleton à la place (ou le créer s'il n'existe pas encore).

2voto

Matt Dillard Points 9040

Cela dépend de ce que tu veux dire par "privé". En Objective-C, l'appel d'une méthode sur un objet peut mieux être décrit comme l'envoi d'un message à l'objet. Il n'y a rien dans la langue, qui interdit à un client à partir de l'appel à la méthode donnée sur un objet; le meilleur que vous pouvez faire est de ne pas déclarer la méthode dans le fichier d'en-tête. Si un client appelle toutefois le "privé" avec la signature à droite, il va encore s'exécuter lors de l'exécution.

Cela dit, la façon 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 tous les "cachés" méthodes là-bas. Rappelez-vous que ce ne sera pas vraiment empêcher les appels d' init de la course, mais le compilateur va cracher des avertissements si quelqu'un essaie de le faire.

MyClass.m

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

@implementation MyClass

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

@end

Il y a un décent thread sur MacRumors.com à propos de ce sujet.

2voto

Peter Lapisu Points 3274

et bien, le problème pourquoi vous ne pouvez pas le faire "privé/invisible" est la cause de la méthode init est envoyé à l'id (alloc retourne un identifiant (id) de ne pas YourClass

Notez que du point de le compilateur (checker) une identification de potencialy répondre à tout ce qui a été tapé (il ne peut pas vérifier ce qui se passe vraiment à l'id au moment de l'exécution), de sorte que vous pouvez le cacher init seulement quand rien nulle part (public = en-tête) utilisent une méthode init, de les compiler le sait, qu'il n'existe aucun moyen pour l'id de répondre à init, car il n'y a pas d'init n'importe où (dans votre source, de toutes les libs etc...)

si vous ne pouvez pas interdire à l'utilisateur de passer d'initialisation et se fait croquer par le compilateur... mais ce que vous pouvez faire, est d'empêcher l'utilisateur de se faire une véritable instance par l'appel d'une init

tout simplement par la mise en œuvre de l'init, qui renvoie nil et ont un (privé / invisible) initialiseur qui nom de quelqu'un d'autre n'obtiendrez pas (comme 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;
}

Remarque : que quelqu'un pourrait le faire

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

et il serait, en effet, renvoyer une nouvelle instance, mais si le initOnce serait nulle part dans notre projet public (en-tête) a déclaré, il génère un avertissement (id peut ne pas répondre ...) et de toute façon la personne à l'aide de cette, aurait besoin de savoir exactement ce que le réel de l'initialiseur est le initOnce

nous pourrions empêcher, mais il n'est pas nécessaire

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X