Références circulaires dans l'iPhone Cocos2D manuel de programmation de jeu

un sujet maltraité mais je ne pouvais pas find de réponse à cela. Je suis le livre "Learn iPhone and iPad cocos2d Game Development" et ne peux pas vraiment comprendre si l'approche de l'exemple ShootEmUp (disponible à 1 ) est la meilleure. L'auteur utilise un GameScene auquel s'ajoute comme object divers objects (par exemple Ship, InputLayer etc.). L'aspect controversé est que dans ces objects il y a des appels à GameScene en utilisant une méthode statique qui returnne une instance statique de la class GameScene qui est instanciée dans la méthode init de GameScene. Cela me semble une reference circulaire et selon beaucoup (par exemple voir ce post) est quelque chose à éviter. Je ne suis pas sûr que cela soit également vrai dans la programmation de jeux car cette approche est trouvée dans 1 et il pourrait y avoir une raison à cela.

Quelqu'un pourrait-il clarifier? Je suis indécis si restructurer mon code complètement ou garder l'approche variable statique.

Merci beaucoup :)!

Code source

Ce que vous voyez ici est un model semi-singleton, et il est largement utilisé dans Cocos, en fait le framework Cocos lui-même est entièrement construit sur des objects singleton (comme beaucoup d'UIKit d'Apple). Les jeux emploient souvent des singletons parce que vous avez généralement beaucoup de données centrales dans un jeu, comme les scores, la santé, les armes, etc. dont beaucoup de vos objects ont besoin. Vous avez également généralement des objects, comme des joueurs, des ennemis, etc. qui ont besoin d'informer le dispatch central de votre application de ce qu'ils font afin que d'autres objects dans le jeu puissent réagir ou s'ajuster en conséquence.

C'est pourquoi de nombreux jeux Cocos utilisent la technique que vous avez montrée ici. Ce n'est pas une mauvaise pratique si vous comprenez les risques de la programmation singleton. Fondamentalement, gardez ceci à l'esprit:

  • Que vous utilisiez une technique de style singleton ou que vous appeliez le parent en utilisant une autre méthode, vous faites essentiellement la même chose dans les deux cas. Il est probablement préférable de referencer directement le moteur de jeu central plutôt que de s'appuyer sur des methods pour le dériver pour vous. Je ne reorderais pas d'utiliser [self parent] car cela peut être difficile à lire et déboguer plus tard quand vous devez d'abord comprendre "qui est le parent", à la place un access singleton vous permet de savoir immédiatement à qui vous accédez.
  • Un enfant ne devrait jamais conserver son parent. Vous pouvez referencer le parent, mais ne le retenez pas.
  • Une alternative à l'approche singleton ici est de faire un iVar dans l'enfant qui pointe vers le parent. Mais c'est essentiellement la même idée, donc pour minimiser les risques d'un cycle de rétention, l'access au singleton est généralement plus sûr. Si votre iVar n'est pas réglé correctement, vous pouvez avoir une reference circulaire. La méthode que vous avez montrée ici n'est pas une reference circulaire.

Notez que ce code particulier vous empêche d'utiliser la méthode +(GameScene*) sharedGameScene que GameScene n'a pas été initialisé. C'est ce qui en fait un semi-singleton. En règle générale, cette méthode dans un singleton sera suffisamment intelligente pour s'initialiser si elle n'est pas déjà initialisée, de sorte que l'utilisation de cette méthode de class renvoie ou crée d'abord, puis renvoie l'object.

Probablement pas un problème dans Cocos puisque vous allez probablement initialiser la Scène de Jeu avant de faire autre chose, donc ça va déjà exister.

Je suppose que vous faites reference à cette partie:

 static GameScene* instanceOfGameScene; +(GameScene*) sharedGameScene { NSAssert(instanceOfGameScene != nil, @"GameScene instance not yet initialized!"); return instanceOfGameScene; } 

Cela ne crée pas de reference circulaire. Certains pourraient argumenter que ce n'est pas une bonne pratique pour build votre code de cette façon, mais c'est une discussion différente.

Si la valeur GameScene par cette fonction (l'object GameScene ) n'est pas référencée comme une propriété forte dans certains enfants GameScene, tout va bien.

Vous auriez le cas de reference circulaire si vous aviez ceci dans l'un des enfants:

 @property(nonatomic, strong) GameScene *mainScene; // OR for non-ARC @property(nonatomic, retain) GameScene *mainScene; 

Celles-ci auraient gardé le nombre de references pour l'object GameScene d'atteindre 0 et de désengager.

J'espère que cela t'aides.