Static NSArray of strings - how/where to initialize in a View Controller

47,271

Solution 1

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

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

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

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

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.

Share:
47,271
Alexander Farber
Author by

Alexander Farber

/me/likes: Java, С#, Perl, PHP, JavaScript, PostgreSQL, Linux, Azure /me/speaks: German, English, Russian /me/learns: https://github.com/afarber/android-questions https://github.com/afarber/unity-questions https://github.com/afarber/ios-questions

Updated on January 21, 2020

Comments

  • Alexander Farber
    Alexander Farber over 4 years

    In a Master-Detail app I'd like to display a TableView with 5 sections titled:

    1. Your Move
    2. Their Move
    3. Won Games
    4. Lost Games
    5. 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:

    enter image description here

    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?)

  • jlehr
    jlehr over 10 years
    That would mean every instance would have its own copy of the array. I don't think that's what the OP was looking for.
  • Simon McLoughlin
    Simon McLoughlin over 10 years
    The user is asking WHERE to create this, not how. You haven't provided any help, the users answer shows him trying to instantiate an object in an interface
  • jlehr
    jlehr over 10 years
    @SimonMcLoughlin Quoting the OP, "My question is however how to init the NSArray _titles?" [emphasis added]
  • Simon McLoughlin
    Simon McLoughlin over 10 years
    fair enough, the point still stands that he's trying to instantiate the object in the interface, this would remove his xcode complier warnings, ill edit with a static array
  • jlehr
    jlehr over 10 years
    If you look carefully, you'll see that the OP posted two different failed attempts, the first of which was an attempt to initialize a global constant with an NSArray.
  • Simon McLoughlin
    Simon McLoughlin over 10 years
    @jlehr and if you read the rest of the question you will see he is getting syntax warnings due to the above mentioned issue. The user is asking for a basic understanding of how to instantiate an object in the flow of a viewController object. Fair point your solution might be the best way to create it, but the user is quite clearly looking for info as to WHERE to create it
  • Hot Licks
    Hot Licks over 10 years
    @jlehr - How many instances of this view controller do you think there are going to be?
  • Simon McLoughlin
    Simon McLoughlin over 10 years
    @HotLicks no that was a valid point, the user did clearly define a static array. The user more than likely had a reason for going out of there way to do that
  • Hot Licks
    Hot Licks over 10 years
    @SimonMcLoughlin - Can you say "premature optimization"?
  • Alexander Farber
    Alexander Farber over 10 years
    Thanks! This seems to be a runtime solution (instead of the compile-time that I was expecting) - is it the best choice for iOS?
  • Alexander Farber
    Alexander Farber over 10 years
    If I use viewDidLoad, then the _titles will be assigned again and again, when I press Home button to send the app to background and then bring it to foreground?
  • jlehr
    jlehr over 10 years
    Objects can't be allocated at compile time, so this is typically what we do. Note that dispatch_once is a Grand Central Dispatch function that ensures the code in the provided block is only run once in the lifetime of the app.
  • Alexander Farber
    Alexander Farber over 10 years
    Thanks, but I've search and it seems (surprisingly!) that this method can be called more than once for a class - when an inheriting class does not implement own +initialize method.
  • Emmanuel
    Emmanuel over 10 years
    @AlexanderFarber Then check that _titles_1 is nil or for the current class in +initialize
  • Alexander Farber
    Alexander Farber over 10 years
    If I check for nil - can't it happen that the +initialize method runs once, but for a wrong class - for the inheriting class?
  • gnasher729
    gnasher729 over 10 years
    +initialize runs once for the class itself, and once for every subclass that doesn't override it. That's why you compare "if (self == [MyIntendedClass class])" first. The check for nil makes it necessary to use synchronisation because two subclasses could be initialised from different threads.
  • Emmanuel
    Emmanuel over 10 years
    @gnasher729 +initialize is thread safe, according Apple documentation : The runtime sends the initialize message to classes in a thread-safe manner.
  • Brody Robertson
    Brody Robertson over 9 years
    You can use the fully qualified class name to call the static method. Instead of [[self class] titles] you can just call [MyClassName titles];
  • jlehr
    jlehr over 9 years
    @BrodyRobertson Targeting a specific instead of obtaining a reference to the class dynamically class breaks inheritance. Also, not sure what you mean by 'fully qualified' class name -- Objective-C doesn't provide namespaces for classes. Did you have something else in mind?
  • Brody Robertson
    Brody Robertson over 9 years
    @jlehr a fully qualified name is an unambiguous name that specifies which object, function, or variable a call refers to without regard to the context of the call. I don't understand what you mean when you say calling a static method via it's qualified classname breaks inheritance.
  • Admin
    Admin about 7 years
    Sometimes, most always the simplest solution is not only the most effective, but the best... Being relatively new to Obj-C (7 years now) I'm still put off by the syntax, but I was pretty sure, a simple static array didn't need to involve 25 lines of code.. My C/C++ background told me this would probably work... NSString *dayNamesArray[7] = {@"blah",@"blah",@"blah",@"blah",@"blah",@"blah",@"and blah"}; I give you credit for the most common sense solution... Thanks!
  • jlehr
    jlehr about 6 years
    Objective-C class methods are not static -- they're dynamically dispatched exactly the same way instance methods are, are inherited, and can be overridden in subclasses.
  • Tiago Mendes
    Tiago Mendes almost 6 years
    with that code you get the error: "Initializer element is not a compile-time constant"