ios cocoa-touch model-view-controller dependencies uiapplicationdelegate

ios - importando AppDelegate



cocoa-touch model-view-controller (7)

Muchas veces inicializo mis variables de clase modelo en mi AppDelegate para que puedan ser utilizadas por diferentes ViewControllers sin pasar su instancia a través de archivos de clase. Sin embargo, cada vez que importo AppDelegate en un archivo .m para acceder a los datos de estas variables siento que estoy haciendo algo mal.

¿Es esta la forma correcta de acceder a estas variables o debería estar haciendo algo diferente?

EDITAR: Mi problema no es cómo acceder a las variables. Actualmente utilizo esta línea de código para obtener mi instancia de appDelegate:

id appDelegate = [[UIApplication sharedApplication] delegate];

Conceptualmente, quiero saber si esta es la forma aceptada de interactuar con las clases modelo de una aplicación. A mi parecer, el AppDelegate de una aplicación administra la aplicación en general. Por lo tanto, parece contrario a la intuición importar esta clase a otras clases más abajo en la cadena de clases de una aplicación.


¿Es esta la forma correcta de acceder a estas variables o debería estar haciendo algo diferente?

Encontrarás que diferentes personas tienen diferentes opiniones sobre esto. El estilo que prefiero es que el delegado de la aplicación pase la información necesaria al primer controlador de vista, y que el controlador de vista la pase a cualquier controlador de vista que cree, y así sucesivamente. La razón principal de esto es que evita que los controladores de vista secundarios dependan de cosas sobre las que no tienen conocimiento.

Si tiene algún editor de detalles, por ejemplo, desea poder pasar ese editor exactamente lo que necesita para hacer su trabajo. Si le da esa información, el editor es completamente flexible: editará cualquier información que usted le dé. Si el editor sabe que debería obtener sus datos de algún objeto externo, como el delegado de la aplicación, pierde cierto grado de flexibilidad: solo puede obtener datos de lo que sabe.

Por lo tanto, está bien configurar su modelo de datos en el delegado de la aplicación. Pero cuando se trata de proporcionar acceso al modelo, piense: diga, no pregunte . Es decir, haga que el delegado de la aplicación le diga al primer controlador de vista qué modelo de objeto usar, y que el controlador le diga al siguiente, y así sucesivamente. Si tiene que preguntar , tiene que saber a quién preguntar, y ahí es donde las dependencias comienzan a dirigirse en la dirección equivocada.

Cada vez que importo AppDelegate en un archivo .m para acceder a los datos de estas variables, siento que estoy haciendo algo mal.

Confía en ese instinto. Piensa por qué se siente mal.


Busque en singletons. Pueden ser una forma más elegante de administrar datos globales.


Debería evitar seriamente la importación del AppDelegate en todas partes y debería sentir que está haciendo algo mal cada vez que lo hace (+1 para eso). Básicamente, estás creando una Gran Bola de Barro y debes reconsiderar tu diseño. Si, por ejemplo, está utilizando CoreData para sus modelos, considere un marco como Magical Panda Active Record para recuperar datos. Trabajo en una aplicación empresarial y el AppDelegate.h solo se incluye en AppDelegate.m .


Esa es una forma de hacerlo, sí, sin embargo no es muy elegante. Los Singletons también son una forma, sí, pero no muy elegantes :) - y no son fáciles de probar su código, si tiene que burlarse de todos sus singletons. En su lugar, lo que probablemente haría es tener un Singleton para un proveedor de servicios y pedirle a este proveedor de servicios una instancia de su proveedor modelo.

Supongamos que su clase de proveedor de servicios es un singleton y necesita acceder al modelo para ver los detalles del usuario. Lo haría de la siguiente manera:

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

Esto significa que crearía una clase JMServiceProvider para registrar servicios y poder recuperar estos servicios. Estos servicios actúan un poco como un singleton; sin embargo, si necesita probar su código por unidad, registrar un servicio diferente que actúe de la misma manera que el original es solo un pedazo de pastel.

Espero que esto responda a su pregunta.

EDITAR: Y lea este artículo: http://martinfowler.com/articles/injection.html - una muy buena que explica las arquitecturas orientadas a servicios también ...


Estoy de acuerdo en que a veces parece que el AppDelegate es el lugar lógico para poner las cosas que solo desea implementar una vez, pero que puede necesitar desde varios lugares. Crear un singleton para cada uno está bien, si esas cosas son complicadas, pero crea muchos archivos adicionales y confunde el proyecto. También estoy de acuerdo con la mayoría de las respuestas aquí, que construir dependencias en AppDelegate es un diseño realmente deficiente .

Creo que la mejor solución es crear un protocolo ! Luego coloque un IBOutlet en una propiedad para hacer lo que necesita haber hecho en cada uno de los controladores que necesitan la función. Los protocolos son la forma estándar de objetivo-C para desacoplar clases.

Entonces, como ejemplo, tal vez tenga una URL de base de datos que pueda necesitar de un montón de lugares. Probablemente, la mejor manera sería establecer una propiedad con cada paso del camino. Pero en algunas situaciones puede ser engorroso por usar un controlador de stock y no querer subclasificarlo. Aquí está mi solución:

Crear archivo: MainDatabasePovider.h

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

Ahora se garantiza que "cualquiera" (es decir, cualquier clase) que diga que implementa el protocolo MainDatabaseProvder proporcionará los dos métodos anteriores. Este podría ser el objeto AppDelegate o CUALQUIER .

Ahora, si quiero que mi AppDelegate proporcione la información, cambio el archivo AppDelegate.h para que tenga:

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

(El único cambio es agregar el protocolo MainDatabaseProvider a la línea @inteface , de nuevo, esto se podría hacer en CUALQUIER clase a la que desee proporcionar la función).

En mi archivo AppDelegate.m tengo que escribir los dos métodos ...

@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

Ahora, en mis controladores u otras clases que necesitan obtener la MainDatabase , agrego lo siguiente:

En sus archivos .h :

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

Esta propiedad se puede configurar en lo que antes se conocía como InterfaceBuilder mediante el control y arrastre, o se puede configurar en el código en prepareForSegue o me gusta proporcionar un captador personalizado que se ajuste de forma predeterminada a AppDelegate en caso de que sea perezoso u olvidadizo y no lo haga. cualquiera de los anteriores. En su archivo .m se vería como:

@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 ); }

Ahora el mainDatabaseProvider puede ser CUALQUIER objeto. Tengo la capacidad de configurarlo en InterfaceBuilder o en mi StoryBoard (aunque realmente no creo que esto sea un elemento de la interfaz de usuario, así que normalmente no lo haría, pero es bastante típico hacerlo de esa manera), puedo configurarlo en código fuera de mi controlador antes de que se cargue en prepareForSegue:sender: o tableView:didSelectRowAtIndexPath: Y si no lo configuro en absoluto, se establecerá de forma predeterminada en AppDelegate si lo he configurado correctamente.

Incluso puedo poner algunos guardias seguros cuando me olvido de hacer cosas en mi vejez cambiando el getter aparece arriba para que me ayude con algo como:

- (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; }


Los estoy importando también y lo uso así:

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

Otra forma puede ser usar un Singleton.


Ponga este método en su clase AppDelegate

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

Y cuando necesite llamar a su AppDelegate use:

[AppDelegate get];