Managing an application’s state can sometimes require complex interaction with persistence and messaging with various resources, or it can be as simple as keeping track of a counter from one view to the next.
Two popular techniques to pass references to objects from one view to the next are to create properties in the Application Delegate, or to continue to pass references from one view to the next like a relay race passes a baton from one runner to the next using a series of carefully placed update methods making for an allocation nightmare and increase the opportunities for memory leaks or the hard to track down crashes. Sometimes this need in programming is referred to as implementing Global Variables. There is also a well established design pattern that can assist with this need as well, it is called the Singleton Pattern.
Singleton Pattern
The Singleton Pattern is a derivative of the Factory Pattern that ensures that one and only one instance of an Object can ever exist. By creating one or more implementations of the Singleton Pattern within a given application, the concept of ‘global variables’ can better be managed through tighter control. This allows for what is called lazy instantiation. If you do not need the variable based on what is going on in the application, then do not ask for or create an instance of one. In Objective-C, Apple has outlined the recommended technique for implementing the singleton pattern.
Objective-C Singleton Pattern
static MySingletonClass *sharedGizmoManager = nil;
But you may find that the following is all that is necessary:
Singleton.h
#import <Foundation/Foundation.h> @interface Singleton : NSObject { } + (Singleton*) retrieveSingleton; @end
Singleton.m
#import "Singleton.h" @implementation Singleton static Singleton *sharedSingleton = nil; + (Singleton*) retrieveSingleton { @synchronized(self) { if (sharedSingleton == nil) { sharedSingleton = [[Singleton alloc] init]; } } return sharedSingleton; } + (id) allocWithZone:(NSZone *) zone { @synchronized(self) { if (sharedSingleton == nil) { sharedSingleton = [super allocWithZone:zone]; return sharedSingleton; } } return nil; } @end
Try and keep each singleton’s scope limited to manage only the information that is related to a particular use case and not as a catch-all for all global information across the application. It is probably best to utilize each singleton as a delegate to the information it is responsible for managing, and not use it as a means to gain access to any objects it has associations with. Although on the iPhone, and when being used in primarily a read only or a write seldom implementation, the risk of writing code that is not thread safe increases when utilizing shared objects. Keeping concurrency in mind, and utilizing the singleton as a delegate to the information at hand, one can watch out for multi thread related issues and deal with them in kind. One thing to watch out for would be include updating or setting properties of the singleton from within an implemented perform selector or a notification. If concurrency issues do arise, it may become necessary to synchronize access to certain properties or methods.
No, not the AppDelegate!
So why not just keep adding properties to the AppDelegate? After all, the AppDelegate is a singleton as well and is therefore accessible by invoking the sharedApplication class method. The problem with this techniques is that you end up loading up the application with too much information that may or may not be necessary depending on what functions the user chooses to evoke. It could also lead to longer and longer startup times. Get the application started as quickly as possible, and don’t leave the user hanging for too long.
What about Global Constants?
Keep in mind that this is not the best technique to employ if all you need is a means to define and gain access to Global Constants. The quickest way to do that is to create a Precompiled Prefix Header file and include that in your project. By default, most of the projects generated in XCode that create iPhone Applications will include a file with an extension of .pch. This file will initially look like the following:
#ifdef __OBJC__ #import <Foundation/Foundation.h> #import <UIKit/UIKit.h> #endif
One can then add any number of #define statements that will be included in all header files across the entire project.
#define SOME_STRING_CONSTANT @"My Important String"
Conclusion
The Singleton Pattern can be used to make the complex and ugly means of sharing a simple variable between two different views or view controls an easy task. If used sparingly and some basic guidelines are followed as not to bloat the application and create a multi thread nightmare to debug, this technique can be quite useful. Much more so than passing Objects back and forth among views or by breaking the encapsulation of the AppDelegate by assigning it more responsibility than it should have.
References
- Mac OS X Reference Library – Cocoa Fundamentals Guide – Creating a Singleton Instance
- iPhone OS Reference Library – UIApplication Class Reference
- Mac OS X Reference Library – Xcode Build system Guide – Using a Precompiled Prefix Header
http://theappleblog.com/2010/05/18/iphone-dev-sessions-using-singletons/
댓글 없음:
댓글 쓰기