Utilisation de soi dans la méthode de la classe
-
14-11-2019 - |
Question
Je suis tombé sur ce code dans Sharekit, et je ne comprends pas ce que l'écrivain avait en tête, en utilisant self
dans une méthode de classe. Il y a un avertissement: des types de pointeurs incompatibles envoyant la «classe» au type de paramètre id<FBSessionDelegate>
. Je veux nettoyer ces avertissements, donc je peux voir ceux qui pourraient faire mal plus tard. Que puis-je / devrais-je faire cela ne cassera pas cela?
C'est le fichier shkfacebook.m et le nom de classe est shkfacebook
+ (void)logout
{
FBSession *fbSession;
if(!SHKFacebookUseSessionProxy){
fbSession = [FBSession sessionForApplication:SHKFacebookKey
secret:SHKFacebookSecret
delegate:self];
}else {
fbSession = [FBSession sessionForApplication:SHKFacebookKey
getSessionProxy:SHKFacebookSessionProxyURL
delegate:self];
}
[fbSession logout];
}
La solution
self
Peut être utilisé dans une méthode de classe comme instance de classe polymorphe.
Par conséquent, la méthode de classe new
peut être implémenté comme ceci:
+ (id)new
{
return [[self alloc] init];
}
et renverrait le type correct pour l'instance de classe qui est envoyée:
Ex A:
NSArray * a = [NSArray new]; // << a is an NSArray
Ex B:
NSMutableArray * a = [NSMutableArray new]; // << a is an NSMutableArray
voir note ci-dessous.
Donc, ce à quoi vous êtes vraiment confronté, c'est de s'assurer qu'il n'y a que des méthodes d'instance dans le protocole, et que les méthodes de Self (de classe) mappent pour adopter les méthodes d'instance dans le protocole.
En ce qui concerne le design ... eh bien, disons simplement que je ne l'aurais pas écrit de cette façon. Un singleton aurait été plus clair et plus correct, mais je n'aime même pas les singletons, donc je n'aurais pas emprunté cette voie.
L'avertissement est produit parce que le Classer l'instance (ce qui est passé) adopte le @protocol
spécifié par le delegate
paramètre. La Class
L'instance n'est pas une instance de la classe. Une déclaration de protocole s'applique vraiment à instances de la classe. Par exemple, si vous adoptez NSLocking
, Le compilateur s'attend-il à ce que vous implémentez également des méthodes de classe pour chaque méthode d'instance déclarée dans le protocole? Réponse: jamais. La mise en œuvre avec laquelle vous avez affaire est IMO, l'un de ces cas où il s'agit d'une mauvaise utilisation de la langue, mais cela fonctionne.
Pour clarifier la terminologie:
La "Class
L'instance "est self
Dans une méthode de classe:
+ (void)foo { self; }
Une "instance de la classe" est self
Dans une méthode d'instance:
- (void)foo { self; }
En pratique, -[NSObject conformsToProtocol:]
est +[NSObject conformsToProtocol:]
et +[NSObject class]
revient juste self
Il n'y a donc pas d'erreurs à l'exécution.
Je ne suis toujours pas clair, alors, pourquoi je reçois un avertissement, si le code répond aux critères que vous avez décrits.
Les critères que j'ai décrits s'appliquent au exécution, mais il s'écarte de la sémantique de la langue - ainsi, le compilateur a absolument raison à ce sujet.
Pour une résolution au problème: il n'y a aucun moyen de dire au compilateur "mon instance de classe est conforme à protocole"Parce que la déclaration d'adoption s'applique à Instances de la classe.
Vous avez deux options principales:
L'approche propre et correcte: utilisez une instance de la classe et implémentez le protocole tel que défini.
ou typast l'instance de classe au protocole:
id delegate = (id) self; fbSession = [fbSession SessionForApplication: shkfacebookkey getSessionProxy: shkfacebookSessionProxyUrl Delegate: Delegate];
Si vous choisissez # 2, cela peut aider à définir les méthodes d'instance du protocole pour utiliser des méthodes de classe, comme ainsi:
+ (void)protocolMethod { /* do stuff */ }
- (void)protocolMethod { [self.class protocolMethod]; }
Cela impliquerait également que vous n'avez jamais besoin d'instances. Cela aiderait car cela ajoutera des avertissements si le protocole change. Ces avertissements bouillonneraient aux méthodes de classe lorsque vous suivez la convention.
Pour réduire le bruit, vous pouvez également envisager de créer une méthode pour réduire le typaste à un seul emplacement:
+ (id<SomeProtocol>)sharedSomeProtocolDelegate
{
return (id<SomeProtocol>)self;
}
- (id<SomeProtocol>)sharedSomeProtocolDelegate
{
return [[self class] sharedSomeProtocolDelegate];
}
Ensuite, vous pouvez simplement écrire:
fbSession = [FBSession sessionForApplication:SHKFacebookKey
getSessionProxy:SHKFacebookSessionProxyURL
delegate:[self sharedSomeProtocolDelegate]];
(Notez que les implémentations de ces types sont en fait des clusters de classe, et vous verrez quelque chose de différent dans le débogueur ou s'il est imprimé)