Static NSArray of strings - how/where to initialize in a View Controller
IosObjective CNsmutablearrayInitializationNsarrayIos Problem Overview
In a Master-Detail app I'd like to display a TableView with 5 sections titled:
- Your Move
- Their Move
- Won Games
- Lost Games
- Options
So I create a blank Master-Detail app in Xcode 5.0.2 and then in its MasterViewController.m (which is a UITableViewController) I'm trying to implement the method:
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return _titles[section];
}
My question is however how to init the NSArray _titles?
I'm trying in the MasterViewController.m:
#import "MasterViewController.h"
#import "DetailViewController.h"
static NSArray *_titles_1 = @[
@"Your Move",
@"Their Move",
@"Won Games",
@"Lost Games",
@"Options"
];
@interface MasterViewController () {
NSMutableArray *_games;
NSArray *_titles_2 = @[
@"Your Move",
@"Their Move",
@"Won Games",
@"Lost Games",
@"Options"
];
}
@end
@implementation MasterViewController
- (void)awakeFromNib
{
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
self.clearsSelectionOnViewWillAppear = NO;
self.preferredContentSize = CGSizeMake(320.0, 600.0);
}
[super awakeFromNib];
}
- (void)viewDidLoad
{
....
}
but both tries above give me syntax errors:
UPDATE:
To my surprise there are many suggestions for this simple question, but as an iOS/Objective-C newbie I'm not sure, which solution is most appropriate.
dispatch_once
- isn't it a runtime operation to execute something once in a multi-threaded app? Isn't it overkill here? I was expecting a compile-time solution for initiating a const array...
viewDidLoad
- when my app changes between background and foreground, wouldn't it unnecessary initiate my const array again and again?
Shouldn't I better set the NSArray
in awakeFromNib
(since I use stroyboard scenes for all my ViewControllers)? Or maybe in initSomething
(is the correct method initWithStyle
?)
Ios Solutions
Solution 1 - Ios
Write a class method that returns the array.
+ (NSArray *)titles
{
static NSArray *_titles;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_titles = @[@"Your Move",
@"Their Move",
@"Won Games",
@"Lost Games",
@"Options"];
});
return _titles;
}
Then you can access it wherever needed like so:
NSArray *titles = [[self class] titles];
Solution 2 - Ios
You can init it in class method +initialize
static NSArray *_titles_1;
@implementation MasterViewController
+ (void)initialize {
_titles_1 = @[
@"Your Move",
@"Their Move",
@"Won Games",
@"Lost Games",
@"Options"
];
}
@end
Solution 3 - Ios
You should declare your static array above @implementation
.
static NSArray *titles_1 = nil;
@implementation ...
And define it in init
or awakeFromNib
or any other method like viewDidLoad
, applicationDidFinishLaunching
wherever you want.
- (void)initMethod{ //change the method name accordingly
if (titles_1 == nil){
[self setTitles_1:@[ @"Your Move", @"Their Move", @"Won Games", @"Lost Games", @"Options" ]];
}
}
Solution 4 - Ios
You can also do something like this:
static NSString * const strings[] = {
[0] = @"string_1",
[1] = @"string_2",
[2] = @"string_3",
[3] = @"string_4",
// ...
};
It's not an NSArray
but you can access NSStrings
like this strings[n]
Solution 5 - Ios
I wonder, if the following would be a good way (answering my own question):
#import "MasterViewController.h"
#import "DetailViewController.h"
static const NSArray *_titles;
@interface MasterViewController () {
NSMutableArray *_objects;
NSMutableArray *_yourMove;
NSMutableArray *_theirMove;
NSMutableArray *_wonGames;
NSMutableArray *_lostGames;
NSMutableArray *_options;
}
@end
@implementation MasterViewController
+ (void)initialize
{
// do not run for derived classes
if (self != [MasterViewController class])
return;
_titles = @[
@"Your Move",
@"Their Move",
@"Won Games",
@"Lost Games",
@"Options"
];
}
This way the const NSArray
is initalized once and right before I need it (in the MasterViewController
class). And the self
check prevents this method from running again - when some inheriting class does not implement its own +initialize
method.
Solution 6 - Ios
You can't instantiate objects in this way, you can only declare them in interfaces. Do the following:
static NSArray *_titles_2;
@interface MasterViewController () {
NSMutableArray *_games;
}
@end
@implementation MasterViewController
-(void)viewDidLoad()
{
_titles_2 = @[
@"Your Move",
@"Their Move",
@"Won Games",
@"Lost Games",
@"Options"
];
}
@end
- or likewise you could use the viewcontroller init method instead of viewDidLoad
Solution 7 - Ios
dispatch_once works. It works in complicated cases, and it works in simple cases. So to be consistent, the best thing is to use it in all cases. That makes it for example a lot easier when you replace all your constant strings with calls to NSLocalizedString (). No code change needed.
Doing things in + (void)initialize isn't really wrong, but I have had situations where I first wanted to configure a class before really using it, and initialize is of course called just before any possible configuration method would start executing. And there are situations where you don't really have a class context. dispatch_once will always work.
Solution 8 - Ios
Why don't use :
+(NSString*)titleForSection:(NSInteger)section {
return (@[ @"title1", @"title2", @"title3" ])[section];
}
Solution 9 - Ios
I prefer to use Objective-C++,change filename from xxx.m
to xxx.mm
,and the initialize of NSArray will happen at runtime
no dispatch_once
,no class method,just write it in one line:
static NSArray *const a = @[@"a",@"b",@"c"];