import de AppDelegate

Souvent, j'initialise mes variables de class de model dans mon AppDelegate afin qu'elles puissent être utilisées par différents ViewControllers sans passer leur instance à travers les files de class. Cependant, chaque fois que j'importe AppDelegate dans un file .m pour accéder aux données de ces variables, j'ai l'printing de faire quelque chose de mal.

Est-ce la bonne façon d'accéder à ces variables ou devrais-je faire quelque chose différemment?

EDIT: Mon problème n'est pas comment accéder aux variables. J'utilise actuellement cette ligne de code pour get mon instance appDelegate:

id appDelegate = [[UIApplication sharedApplication] delegate];

Conceptuellement, je veux savoir si c'est la façon acceptée d'interagir avec les classs de templates d'une application. Il me semble que AppDelegate d'une application gère globalement l'application. Il semble donc contre-intuitif d'importer cette class dans d'autres classs plus bas dans la string de class d'une application.

Est-ce la bonne façon d'accéder à ces variables ou devrais-je faire quelque chose différemment?

Vous constaterez que différentes personnes ont des opinions différentes à ce sujet. Le style que je préfère est que le délégué de l'application transmette les informations nécessaires au premier controller de vue, et que le controller de vue le transmette à tous les controllers de vue qu'il crée, et ainsi de suite. La principale raison à cela est que cela empêche les controllers de vue enfants de dépendre de choses dont ils n'ont aucune connaissance.

Si vous avez un éditeur de détail, par exemple, vous voulez pouvoir transmettre à cet éditeur exactement ce dont il a besoin pour faire son travail. Si vous lui donnez cette information, le rédacteur est complètement flexible – il éditera n'importe quelle information que vous lui donnez. Si l'éditeur sait qu'il doit récupérer ses données à partir d'un object externe, comme le délégué de l'application, il perd alors un peu de flexibilité – il ne peut get que des données de la chose qu'il connaît.

Ainsi, vous pouvez configurer votre model de données dans le délégué de l'application. Mais quand il s'agit de donner access au model, pensez: dites, ne requestz pas . C'est-à-dire que le délégué de l'application indique au premier controller de vue quel object de model utiliser et que ce controller indique le suivant et ainsi de suite. Si vous devez requestr , vous devez savoir à qui requestr, et c'est là que les dependencies commencent à se diriger dans la mauvaise direction.

Chaque fois que j'importe AppDelegate dans un file .m pour accéder aux données de cette variable, j'ai l'printing de faire quelque chose de mal.

Faites confiance à cet instinct. Pensez à la raison pour laquelle vous vous sentez mal.

Je suis d'accord que parfois, il semble que AppDelegate est l'endroit logique pour mettre des choses que vous voulez seulement implémenter une fois mais peut avoir besoin de plusieurs endroits. Créer un singleton pour chacun est bien, si ces choses sont compliquées, mais cela crée beaucoup de files supplémentaires et de confusion pour le projet. Je suis également d'accord avec la majorité des réponses ici, que la construction de dependencies sur AppDelegate est un très mauvais design .

Je pense que la meilleure solution est de créer un protocole ! Ensuite, placez un IBOutlet sur une propriété pour faire ce que vous devez faire dans chacun des controllers qui ont besoin de la fonction. Les protocoles sont la méthode standard de l'objective-C pour découpler les classs.

Ainsi, à titre d'exemple, j'ai peut-être une URL de database dont j'ai besoin d'un tas d'endroits. Probablement le meilleur moyen serait de définir une propriété avec chaque étape en cours de route. Mais dans certaines situations, cela peut être fastidieux en raison de l'utilisation d'un controller de stock et ne voulant pas le sous-classr. Voici ma solution:

Créer un file: MainDatabasePovider.h

 #import <Foundation/Foundation.h> @protocol MainDatabaseProvider <NSObject> @required @property (nonatomic, readonly) NSURL *applicationDocumentsDirectory; @property (nonatomic, weak) NSURL *mainDatabase; @end 

Maintenant "n'importe qui" (c'est-à-dire n'importe quelle class) qui dit qu'il implémente le protocole MainDatabaseProvder est garanti pour fournir les deux methods ci-dessus. Cela peut être l'object AppDelegate ou ANY .

Maintenant, si je veux que mon AppDelegate fournisse les informations, je change le file AppDelegate.h pour avoir:

 #import "MainDatabaseProvider.h" @interface AppDelegate : UIResponder <UIApplicationDelegate, MainDatabaseProvider> @property (strong, nonatomic) UIWindow *window; @end 

(Le seul changement est d'append le protocole MainDatabaseProvider à la ligne @inteface , encore une fois cela pourrait être fait à n'importe @inteface class que vous voulez fournir la fonction).

Dans mon file AppDelegate.m je dois écrire les deux methods …

 @implementation AppDelegate ... @synthesize mainDatabase = _mainDatabase; ... - (NSURL *) applicationDocumentsDirectory { return [[[NSFileManager defaultManager] URLsForDirectory: NSDocumentDirectory inDomains: NSUserDomainMask] lastObject]; } - (void) setMainDatabase: (NSURL *) mainDatabase { if( _mainDatabase != mainDatabase ) { _mainDatabase = mainDatabase; } } - (NSURL *) mainDatabase { if( !_mainDatabase ) { NSURL *docURL = self.applicationDocumentsDirectory; self.mainDatabase = [docURL URLByAppendingPathComponent: @"My Great Database"]; } return _mainDatabase; } ... @end 

Maintenant, dans mes controllers ou d'autres classs qui ont besoin d'get la base de données principale, j'ai ajouté ce qui suit:

Dans leurs files .h :

 #import "MainDatabaseProvider.h" ... @interface myGreatViewController: UIViewController @property (nonatomic, weak) IBOutlet id <MainDatabaseProvider> mainDatabaseProvider; ... @end 

Cette propriété peut être définie dans ce qui était auparavant connu sous le nom InterfaceBuilder en faisant glisser le contrôle ou peut être définie dans le code dans prepareForSegue ou j'aime fournir un accesseur personnalisé par défaut à AppDelegate au cas où je suis fainéant ou oublieux et ne fais pas l'un ou l'autre de ce qui précède. Dans leur file .m , cela ressemblerait à:

 @implementation myGreatViewController @synthesize mainDatabaseProvider = _mainDatabaseProvider; ... - (id <MainDatabaseProvider>) mainDatabaseProvider { id appDelegate = [[UIApplication sharedApplication] delegate]; if( !_mainDatabaseProvider && [appDelegate conformsToProtocol: @protocol(MainDatabaseProvider)] ) return appDelegate; return _mainDatabaseProvider; } // To get the database URL you would just do something like... - (void) viewWillAppear: (BOOL) animated { NSLog( @"In %s the mainDatabaseProvider says the main database is \"%@\"", __func__, self.mainDatabaseProvider.mainDatabase.path ); } 

mainDatabaseProvider peut maintenant être n'importe quel object. J'ai la possibilité de le paramétrer dans InterfaceBuilder ou dans ma StoryBoard (bien que je ne pense pas vraiment que ce soit un élément d'interface user donc je ne le ferais pas mais c'est assez typique de le faire de cette façon), je peux le définir dans le code en dehors de mon controller avant qu'il ne soit chargé dans prepareForSegue:sender: ou tableView:didSelectRowAtIndexPath: Et si je ne le mets pas du tout, il apparaîtra par défaut sur AppDelegate si je l'ai configuré correctement.

Je peux même mettre des gardes de security pour quand j'oublie de faire des choses dans ma vieillesse en changeant le getter énuméré ci-dessus pour m'aider avec quelque chose comme:

 - (id <MainDatabaseProvider>) mainDatabaseProvider { if( !_mainDatabaseProvider ) { id appDelegate = [[UIApplication sharedApplication] delegate]; if( ![appDelegate conformsToProtocol: @protocol(MainDatabaseProvider)] ) { NSLog( @"Hey!! The mainDatabaseProvider is not set and the AppDelegate does not conform to the MainDatabaseProvider protocol. How do you expect me to figure out where the database is!" ); } else { return appDelegate; } return _mainDatabaseProvider; } 

Vous devriez sérieusement éviter d'importer l'AppDelegate partout et vous devriez avoir l'printing de faire quelque chose de mal chaque fois que vous le faites (+1 pour cela). Vous créez essentiellement une grosse boule de boue et devriez reconsidérer votre design. Si par exemple vous utilisez CoreData pour vos templates, envisagez un framework tel que Magical Panda Active Record pour récupérer des données. Je travaille sur une application d'entreprise et AppDelegate.h est seulement inclus dans AppDelegate.m .

Je les importe aussi et je l'utilise comme ceci:

  AppDelegate *delegate = (AppDelegate*)[[UIApplication sharedApplication] delegate]; [delegate variable]; 

Une autre façon peut être d'utiliser un singleton.

Mettez cette méthode dans votre class AppDelegate

 + (AppDelegate *)get { return (AppDelegate *) [[UIApplication sharedApplication] delegate]; } 

Et lorsque vous devez appeler votre application AppDelegate:

 [AppDelegate get]; 

C'est une façon de le faire, oui, mais ce n'est pas très élégant. Les singletons sont un moyen aussi, oui, mais pas très élégant 🙂 – et vraiment pas facile de tester votre code, si vous avez besoin de se moquer de tous vos singletons. Au lieu de cela, ce que je ferais probablement est d'avoir un singleton pour un fournisseur de services, et de requestr à ce fournisseur de services une instance de votre fournisseur de model.

Supposons que votre class de fournisseur de services soit un singleton et que vous ayez besoin d'accéder au model pour voir les détails de l'user. Je le ferais de la manière suivante:

 JMUserDetailModel *myModel = [[[JMServiceProvider sharedInstance] modelProvider] userDetailModel]; 

Cela signifie que vous devez créer une class JMServiceProvider pour save des services et pouvoir récupérer ces services. Ces services agissent un peu comme un singleton, cependant si vous avez besoin de tester votre code unitaire, alors l'logging d'un service différent qui agit de la même manière que l'original n'est qu'un jeu d'enfant.

J'espère que ça répond à ta question.

EDIT: Et lisez cet article: http://martinfowler.com/articles/injection.html – une très bonne explication des architectures orientées service aussi …

Regardez dans singletons. Ils peuvent être une manière plus élégante de gérer datatables globales.