Comment puis-je implémenter le comportement comme dans le model de cluster par Apple (NSSsortingng et NSCFSsortingng)

J'écris simplement le code suivant à des fins de test:

NSSsortingng *aStr = [[NSSsortingng alloc] initWithFormat:@"Foo"]; aStr = [aStr initWithFormat:@"Bar"];//Crashed here 

Je reçois l'erreur suivante:

 *** initialization method -initWithFormat:locale:arguments: cannot be sent to an abstract object of class __NSCFSsortingng: Create a concrete instance! 

Si j'écris le code suivant la même chose arrive

 NSSsortingng *aStr = [NSSsortingng alloc]; aStr = [aStr initWithFormat:@"Foo"]; aStr = [aStr initWithFormat:@"Bar"]; //Crashed here 

Par google, je viens de savoir que initWithFormat returnnera l'object NSCFSsortingng . Ma question est si NSCFSsortingng est une class dérivée de NSSsortingng alors pourquoi je ne peux pas invoquer la méthode NSCFSsortingng sur NSCFSsortingng . S'il est possible d'arrêter la visibilité, comment puis-je l'implémenter dans le code?

Faisons des searchs sur le fonctionnement interne du cluster de classs NSSsortingng :

 NSSsortingng *factory = [NSSsortingng alloc]; NSSsortingng *theInstance = [factory initWithSsortingng:@"I am constant"]; NSLog(@"factory class: %@, instance class: %@", [factory class], [theInstance class]); 

Et la sortie est:

 factory class: NSPlaceholderSsortingng, instance class: __NSCFConstantSsortingng 

Comme vous pouvez le voir, la méthode alloc renvoie une instance de NSPlaceholderSsortingng . C'est une class "factory" qui implémente toutes les methods init... déclarées dans NSSsortingng . Ces methods renvoient des sous-classs concrètes (privées) de NSSsortingng . Il renvoie __NSCFConstantSsortingng dans cet exemple.

Si vous changez la première ligne à

 NSSsortingng *factory = [NSMutableSsortingng alloc]; 

la sortie changera pour:

NSPlaceholderMutableSsortingng, class d'instance: __NSCFSsortingng

Il existe donc différentes classs d'usine pour les strings mutables et immuables, et ces usines renvoient des sous-classs différentes.

Vous pouvez même vérifier la hiérarchie des sous-classs privées dans les en-têtes d'exécution iOS: ici et ici .


Voyons maintenant ce qui se passe quand nous appelons initWithSsortingng: sur une instance de __NSCFConstantSsortingng nous venons de créer.

 [theInstance initWithSsortingng:@"Crash"]; 

Comme vous l'attendiez – il se bloque. Dans la stack, nous pouvons voir que -[NSSsortingng initWithCharactersNoCopy:length:freeWhenDone:] est appelée, en lançant une exception:

'NSInvalidArgumentException', raison: '*** méthode d'initialisation -initWithCharactersNoCopy: longueur: freeWhenDone: ne peut pas être envoyée à un object abstrait de la class __NSCFConstantSsortingng: Crée une instance concrète!'

Nous pouvons donc supposer que cet initialiseur dans la class NSSsortingng est en fait une méthode abstraite (il n'y a pas de methods abstraites dans Objective-C, donc il lance une exception quand il est appelé).

Cette méthode est implémentée dans la class d'usine NSPlaceholderSsortingng . Mais il n'est pas implémenté dans toutes les sous-classs concrètes, donc si vous appelez l'une des methods init... , il appellera l'implémentation de NSSsortingng qui lève l'exception.


Mettons tout set et construisons une petite partie du cluster de la class NSSsortingng . C'est vraiment simplifié et probablement totalement différent de l'implémentation réelle, mais je voulais juste montrer l'idée.

 @interface NSPlaceholderSsortingng : NSSsortingng @end @interface __NSCFConstantSsortingng : NSSsortingng @end @implementation NSSsortingng + (instancetype)alloc { return [[NSPlaceholderSsortingng alloc] init]; } - (instancetype)initWithCharactersNoCopy:(unichar *)characters length:(NSUInteger)length freeWhenDone:(BOOL)freeBuffer { [NSException raise:NSInvalidArgumentException format:@" initialization method -initWithCharactersNoCopy:length:freeWhenDone: cannot be sent to an abstract object of class %@: Create a concrete instance!'", [self class]]; return nil; } - (instancetype)initWithSsortingng:(NSSsortingng *)aSsortingng { //this method has to call the "abstract" initializer somewhere. The real implementation is probably more complex, this single line is here for simplicity return [self initWithCharactersNoCopy:[aSsortingng UTF8Ssortingng] length:[aSsortingng length] freeWhenDone:YES]; } @end @implementation NSPlaceholderSsortingng - (instancetype)initWithCharactersNoCopy:(unichar *)characters length:(NSUInteger)length freeWhenDone:(BOOL)freeBuffer { __NSCFConstantSsortingng *concreteClassInstance = ...; // create the concrete instance. return concreteClassInstance; } @end @implementation __NSCFConstantSsortingng //implement all the needed methods here. But do NOT implement initWithCharactersNoCopy:length:freeWhenDone: @end 

NSCFSsortingng interfaces NSCFSsortingng et NSCFConstantSsortingng sont privées et vous ne devriez pas les utiliser. En regardant votre code, il est difficile de discerner pourquoi vous auriez besoin de plonger dans les sous-classs privées quand la super class NSSsortingng publique fait tout ce dont vous avez besoin d'une manière encore plus simple:

 NSSsortingng *aStr = @"Foo"; aStr = @"Bar"; 

Ou, si vous avez besoin d'utiliser le format:

 NSSsortingng *aStr = [NSSsortingng ssortingngWithFormat:@"Foo"]; 

Le problème est que vous ne pouvez pas réinitialiser un NSSsortingng puisqu'il s'agit d'une class non mutable, si vous voulez changer un NSSsortingng après la création, vous devez utiliser NSMutableSsortingng .

Cependant, dans votre cas, vous pouvez également utiliser un NSSsortingng , comme ceci:

 NSSsortingng *aStr = [[NSSsortingng alloc] initWithFormat:@"Foo"]; aStr = [[NSSsortingng alloc] initWithFormat:@"Bar"]; 

Cependant, mieux serait:

 NSSsortingng *aStr = @"Foo"; aStr = @"Bar"; 

si ce que vous essayez de faire est d'append la string que vous feriez:

 NSMutableSsortingng *aStr = [[NSMutableSsortingng alloc] initWithSsortingng:@"Foo"]; [aStr appendSsortingng:@"Bar"]; 

Un cluster de class est généralement implémenté avec une class publique et de nombreuses sous-classs privées dérivées de la class publique afin qu'elles aient la même interface. Vérifiez NSNumber. En impliquant [NSNumber numberWithBool]; cette méthode renvoie une instance d'une sous-class de NSNumber spécifique à bool tandis que [NSNumber numberWithInt]; renvoie une instance d'une sous-class de NSNumber spécifique à nit's. Les deux chard la même interface, l'interface NSNumber.

Les methods init n'ont pas besoin de returnner le même object. Pour implémenter le même comportement, écrivez simplement

 - (instancetype)initWithSomeArguments { if ((self = [super initWithSomeArguments) != nil) { self = [[RelatedClass alloc] initWithSomeArguments]; } return self; }