web-dev-qa-db-fra.com

NSArray statique de chaînes - comment / où initialiser dans un contrôleur de vue

Dans une application Master-Detail, je voudrais afficher une TableView avec 5 sections intitulées:

  1. Votre déménagement
  2. Leur déménagement
  3. Jeux gagnés
  4. Jeux perdus
  5. Les options

Je crée donc une application Master-Detail vierge dans Xcode 5.0.2 puis dans son MasterViewController.m (qui est un UITableViewController) j'essaie d'implémenter la méthode:

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    return _titles[section];
}

Ma question comment faire pour initier les _titres NSArray?

J'essaye dans le 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
{
     ....
}

mais les deux essais ci-dessus me donnent des erreurs de syntaxe:

enter image description here

MISE À JOUR:

À ma grande surprise, il existe de nombreuses suggestions pour cette question simple, mais en tant que débutant iOS/Objective-C, je ne sais pas quelle solution est la plus appropriée.

dispatch_once - n'est-ce pas une opération d'exécution d'exécuter quelque chose une fois dans une application multi-thread? N'est-ce pas exagéré ici? Je m'attendais à une solution au moment de la compilation pour lancer un tableau const ...

viewDidLoad - lorsque mon application passe de l'arrière-plan au premier plan, ne serait-il pas inutile de lancer mon tableau const à plusieurs reprises?

Ne devrais-je pas mieux définir le NSArray dans awakeFromNib (puisque j'utilise des scènes stroyboard pour tous mes ViewControllers)? Ou peut-être dans initSomething (est la bonne méthode initWithStyle?)

46
Alexander Farber

Écrivez une méthode de classe qui renvoie le tableau.

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

Ensuite, vous pouvez y accéder où vous en avez besoin, comme suit:

NSArray *titles = [[self class] titles];
79
jlehr

Vous pouvez l'initier dans la méthode de classe + initialize

static NSArray *_titles_1;

@implementation MasterViewController
+ (void)initialize {
    _titles_1 = @[
        @"Your Move",
        @"Their Move",
        @"Won Games",
        @"Lost Games",
        @"Options"
    ];
}
@end
12
Emmanuel

Vous pouvez également faire quelque chose comme ceci:

static NSString * const strings[] = {
        [0] = @"string_1",
        [1] = @"string_2",
        [2] = @"string_3",
        [3] = @"string_4",
        // ...
    };

Ce n'est pas un NSArray mais vous pouvez accéder à NSStrings comme ceci strings[n]

7
Pablo A.

Vous devez déclarer votre tableau statique au-dessus de @implementation.

static NSArray *titles_1 = nil;

@implementation ...

Et définissez-le dans init ou awakeFromNib ou toute autre méthode comme viewDidLoad, applicationDidFinishLaunching où vous voulez.

- (void)initMethod{ //change the method name accordingly

    if (titles_1 == nil){
        [self setTitles_1:@[ @"Your Move", @"Their Move", @"Won Games", @"Lost Games", @"Options" ]];
    }
}
7
Anoop Vaidya

Je me demande si ce qui suit serait un bon moyen (répondant à ma propre 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"
    ];
}

De cette façon, le const NSArray est initialisé une fois juste avant que j'en ai besoin (dans la classe MasterViewController). Et la vérification self empêche cette méthode de s'exécuter à nouveau - lorsqu'une classe héritant n'implémente pas sa propre +initialize méthode.

6
Alexander Farber

dispatch_once fonctionne. Cela fonctionne dans des cas compliqués et cela fonctionne dans des cas simples. Donc pour être cohérent, le mieux est de l'utiliser dans tous les cas. Cela rend par exemple beaucoup plus facile lorsque vous remplacez toutes vos chaînes constantes par des appels à NSLocalizedString (). Aucun changement de code nécessaire.

Faire des choses dans + (void) initialize n'est pas vraiment mal, mais j'ai eu des situations où je voulais d'abord configurer une classe avant de vraiment l'utiliser, et initialize est bien sûr appelé juste avant qu'une méthode de configuration possible ne commence à s'exécuter. Et il y a des situations où vous n'avez pas vraiment de contexte de classe. dispatch_once fonctionnera toujours.

2
gnasher729

Vous ne pouvez pas instancier des objets de cette manière, vous ne pouvez les déclarer que dans des interfaces. Procédez comme suit:

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
  • ou de même, vous pouvez utiliser la méthode init de viewcontroller au lieu de viewDidLoad
2
Simon McLoughlin