Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (8.37 MB, 640 trang )
static MyWorld
*sharedInstance = nil;
- (id) init {
self = [super init];
if (self != nil){
/* Do NOT allocate/initialize other objects here
that might use the MyWorld's sharedInstance as
that will create an infinite loop */
}
return(self);
}
- (void) initializeSharedInstance{
/* Allocate/initialize your values here as we are
sure this method gets called only AFTER the
instance of MyWorld has been created through
the [sharedInstance] class method */
}
+ (MyWorld *)
sharedInstance{
@synchronized(self){
if (sharedInstance == nil){
sharedInstance = [[self alloc] init];
/* Now initialize the shared instance */
[sharedInstance initializeSharedInstance];
}
return(sharedInstance);
}
}
- (NSUInteger) retainCount{
return(NSUIntegerMax);
}
- (void) release{
/* Don't call super here. The shared instance should
not be deallocated */
}
- (id) autorelease{
return(self);
}
- (id) retain{
return(self);
}
2.8 Incorporating and Using Models in the GUI | 51
www.it-ebooks.info
- (void) dealloc {
[super dealloc];
}
@end
In your views and view controllers or other nonmodel objects, you can retrieve an
instance to your model using the sharedInstance class method of MyWorld:
- (void)viewDidLoad {
[super viewDidLoad];
MyWorld *bigWorld = [MyWorld sharedInstance];
MyWorld *theSameWorld = [MyWorld sharedInstance];
if ([bigWorld isEqual:theSameWorld] == YES){
NSLog(@"The Big World and the Same World are the same!");
} else {
NSLog(@"These two worlds are different.");
}
}
Both bigWorld and theSameWorld now point to the same instance in memory. This is
one of the reasons you might consider creating a shared instance—or what is traditionally called a singleton—whenever you are designing a model or storage of some type
that has to be accessed from multiple objects across a project and you do not want to
keep a reference to the same instance of the object anywhere specific. Using a class
method providing you with a shared instance of an object, you will easily be able to
call this method anywhere using only the name of your class, as shown previously, in
order to obtain a reference to the one and only copy of this object in memory.
A shared instance of an object has a retain count of 1 and should not,
in any circumstances, be retained since that will impose complications
on how the shared instance is maintained.
If you had a good look at the implementation of MyWorld, you must have noticed the
comments in the init and initializeSharedInstance methods. The reason we should
not allocate and initialize important resources in the init method of MyWorld is that if
any of the classes that might be allocated and initialized in the init method of
MyWorld access the sharedInstance method of MyWorld, the sharedInstance class method
will be called prematurely without having already returned from its previous init
method. Therefore, we will be in an infinite loop of calling the sharedInstance
class method since none of the sharedInstance class methods can complete without
depending on another sharedInstance call. This can be demonstrated better with an
example:
52 | Chapter 2: Implementing Controllers and Views
www.it-ebooks.info
1. A view controller calls the sharedInstance class method of MyWorld for the first time
in our application. We have not called this class method anywhere else yet.
2. The sharedInstance class method determines that the sharedInstance static variable is nil, and therefore attempts to allocate and initialize this static variable by
calling the alloc and init methods of the MyWorld object.
3. Imagine we instantiate an object of some type in the init instance method of
MyWorld, by calling the alloc and init methods of that object. Now imagine that
that object’s init method attempts to initialize itself using a variable that is inside
MyWorld. Therefore, the object will call the sharedInstance class method of
MyWorld to get a reference to MyWorld. However, the problem here is that we are
still not out of the init instance method of MyWorld explained in step 2. We are still
trying to finish the init method, but the initialization of our new object was in the
init method and needs to be finished, and since this new initialization depends on
MyWorld itself, another sharedInstance class method will be called and MyWorld will
be allocated and initialized again. Another initialization means an infinite loop to
steps 2 and 3, until the end of time!
To demonstrate this fragile fact, change the init instance method of MyWorld to the
following implementation:
- (id) init {
self = [super init];
if (self != nil){
/* Do NOT allocate/initialize other objects here
that might use the MyWorld's sharedInstance as
that will create an infinite loop */
[MyWorld sharedInstance];
}
return(self);
}
Now if you try to run your application, it will crash. To fix this issue, add the code
shown in bold to the initializeSharedInstance instance method:
- (void) initializeSharedInstance{
/* Allocate/initialize your values here as we are
sure this method gets called only AFTER the
instance of MyWorld has been created through
the [sharedInstance] class method */
[MyWorld sharedInstance];
}
2.8 Incorporating and Using Models in the GUI | 53
www.it-ebooks.info
+ (MyWorld *)
sharedInstance{
@synchronized(self){
if (sharedInstance == nil){
sharedInstance = [[self alloc] init];
/* Now initialize the shared instance */
[sharedInstance initializeSharedInstance];
}
return(sharedInstance);
}
}
2.9 Implementing Navigation Bars
Problem
You want to be able to present options to your users and maintain a stack-like array of
screens that users can traverse easily.
Solution
Use a UINavigationController class to organize the hierarchy of view controllers in your
application. A navigation controller will create a navigation bar for you automatically:
#import
@class ViewsAndVCViewController;
@interface ViewsAndVCAppDelegate : NSObject
@public
UIWindow *window;
UINavigationController *navigationController;
}
@property (nonatomic, retain)
IBOutlet UIWindow *window;
@property (nonatomic, retain)
UINavigationController *navigationController;
@end
As you can see, we have declared our navigation controller in our application delegate.
You do not have to do this in the application delegate. In fact, you can instantiate an
object of type UINavigationController anywhere in your application. We will now implement the application delegate:
#import "ViewsAndVCAppDelegate.h"
#import "FirstViewController.h"
@implementation ViewsAndVCAppDelegate
54 | Chapter 2: Implementing Controllers and Views
www.it-ebooks.info
@synthesize window;
@synthesize navigationController;
- (BOOL) application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
FirstViewController *controller =
[[FirstViewController alloc]
initWithNibName:@"FirstViewController"
bundle:nil];
UINavigationController *theNavigationController =
[[UINavigationController alloc]
initWithRootViewController:controller];
self.navigationController = theNavigationController;
[theNavigationController release];
[self.navigationController setNavigationBarHidden:NO
animated:YES];
[controller release];
// Add the view controller's view to the window and display.
[window addSubview:self.navigationController.view];
[window makeKeyAndVisible];
}
return YES;
- (void)dealloc {
[navigationController release];
[window release];
[super dealloc];
}
@end
Discussion
There are two good ways to create navigation controllers in an application for the
iPhone or iPad:
• Creating a navigation-based application
• Creating a UINavigationController object manually in the application delegate
I choose the second method over the first, for various reasons. One is that I feel I get
more control over how the structure of my application is created from the beginning;
another is that the second method makes it easier for me to control the lifetime of the
navigation controller.
2.9 Implementing Navigation Bars | 55
www.it-ebooks.info