字符串的静态 NSArray - 如何/在哪里初始化视图控制器

Posted

技术标签:

【中文标题】字符串的静态 NSArray - 如何/在哪里初始化视图控制器【英文标题】:Static NSArray of strings - how/where to initialize in a View Controller 【发布时间】:2013-12-12 13:17:24 【问题描述】:

在 Master-Detail 应用程序中,我想显示一个包含 5 个部分的 TableView,标题为:

    你的举动 他们的行动 赢得比赛 输掉的比赛 选项

所以我在 Xcode 5.0.2 中创建了一个空白的 Master-Detail 应用程序,然后在它的 MasterViewController.m(这是一个 UITableViewController)中尝试实现该方法:

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

我的问题是如何初始化 NSArray _titles?

我正在 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

     ....

但上面的两次尝试都给我语法错误:

更新:

令我惊讶的是,对于这个简单的问题有很多建议,但作为 ios/Objective-C 新手,我不确定哪种解决方案最合适。

dispatch_once - 在多线程应用程序中执行一次不是运行时操作吗?这里是不是太过分了?我期待一个用于启动 const 数组的编译时解决方案...

viewDidLoad - 当我的应用在后台和前台之间切换时,是否不需要一次又一次地启动我的 const 数组?

我不应该在awakeFromNib 中设置NSArray 更好吗(因为我的所有视图控制器都使用了stroyboard 场景)?或者可能在initSomething(是正确的方法initWithStyle?)

【问题讨论】:

viewDidLoad初始化它。 您应该将签名改为static NSArray* const。有很多方法可以解决何时初始化数组,如下面的答案所示。我喜欢惰性初始化,但它并不适合这个用例。 您正在寻找的是(窒息!)单例式初始化。它不需要在自己的类中,但是,它可以是此类的一部分。 @AlexanderFarber iOS 应用程序是多线程的,并且类方法可以被多个线程并发调用,因此使用dispatch_once 或某种形式的锁定会更安全。我可以从第一手经验告诉你,否则崩溃错误的可能性是非常真实的。 【参考方案1】:

编写一个返回数组的类方法。

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

然后您可以在任何需要的地方访问它,如下所示:

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

【讨论】:

用户询问在哪里创建这个,而不是如何创建。您没有提供任何帮助,用户的回答显示他试图在界面中实例化一个对象 @SimonMcLoughlin 引用 OP,“但是我的问题是 如何 初始化 NSArray _titles?” [强调] @jlehr 如果您阅读问题的其余部分,您会看到由于上述问题,他收到了语法警告。用户要求基本了解如何在 viewController 对象的流中实例化对象。公平点您的解决方案可能是创建它的最佳方式,但用户非常清楚地在寻找有关在何处创建它的信息 无法在编译时分配对象,所以这通常是我们所做的。请注意,dispatch_once 是一个 Grand Central Dispatch 函数,可确保提供的块中的代码在应用程序的生命周期内只运行一次。 可以使用全限定类名来调用静态方法。而不是 [[self class]titles] 你可以调用 [MyClassNametitles];【参考方案2】:

你可以在类方法+initialize中初始化它

static NSArray *_titles_1;

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

@end

【讨论】:

谢谢,但是我已经搜索过了,似乎(令人惊讶!)这个方法可以为一个类调用多次——当继承类没有实现自己的 +initialize i> 方法。 @AlexanderFarber 然后检查 _titles_1 是否为 nil 或 +initialize 中的当前类 如果我检查nil - 难道+initialize 方法运行一次,但是对于一个错误的类 - 对于继承类? +initialize 为类本身运行一次,并为每个不覆盖它的子类运行一次。这就是您首先比较“if (self == [MyIntendedClass class])”的原因。对 nil 的检查使得必须使用同步,因为可以从不同的线程初始化两个子类。 @gnasher729 +initialize 是线程安全的,根据 Apple 文档:The runtime sends the initialize message to classes in a thread-safe manner.【参考方案3】:

你应该在@implementation上方声明你的静态数组。

static NSArray *titles_1 = nil;

@implementation ...

并在initawakeFromNib 或任何其他方法(如viewDidLoadapplicationDidFinishLaunching)中任意定义。

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

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

【讨论】:

【参考方案4】:

你也可以这样做:

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

这不是NSArray,但您可以像这样访问NSStrings strings[n]

【讨论】:

有时,最简单的解决方案不仅是最有效的,而且是最好的......对 Obj-C 来说相对较新(7 年了)我仍然对语法感到厌烦,但我很确定,一个简单的静态数组不需要涉及 25 行代码。我的 C/C++ 背景告诉我这可能会工作... NSString *dayNamesArray[7] = @"blah",@ "废话",@"废话",@"废话",@"废话",@"废话",@"废话";我为您提供了最常识性的解决方案...谢谢!【参考方案5】:

我想知道,以下是否是一个好方法(回答我自己的问题):

#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"
    ];

这样const NSArray 在我需要它之前就被初始化一次(在MasterViewController 类中)。并且self 检查会阻止该方法再次运行——当某些继承类没有实现自己的+initialize 方法时。

【讨论】:

【参考方案6】:

你不能用这种方式实例化对象,你只能在接口中声明它们。执行以下操作:

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
或者同样您可以使用 viewcontroller init 方法而不是 viewDidLoad

【讨论】:

这意味着每个实例都有自己的数组副本。我认为这不是 OP 想要的。 很公平,重点仍然是他正在尝试在界面中实例化对象,这将消除他的 xcode 编译器警告,使用静态数组进行错误编辑 如果你仔细看,你会看到 OP 发布了两次不同的失败尝试,其中第一次尝试使用 NSArray 初始化全局常量。 @jlehr - 你认为这个视图控制器会有多少个实例? @HotLicks 不,这是一个有效的观点,用户确实明确定义了一个静态数组。用户很可能有理由不这样做【参考方案7】:

dispatch_once 有效。它适用于复杂的情况,也适用于简单的情况。所以要保持一致,最好的办法是在所有情况下都使用它。例如,当您将所有常量字符串替换为对 NSLocalizedString () 的调用时,这会变得容易得多。无需更改代码。

在 + (void)initialize 中做事并没有真正的错误,但是我遇到过这样的情况,我首先想在真正使用它之前配置一个类,并且在任何可能的配置方法开始执行之前当然会调用 initialize .在某些情况下,您实际上并没有类上下文。 dispatch_once 将始终有效。

【讨论】:

【参考方案8】:

为什么不使用:

+(NSString*)titleForSection:(NSInteger)section 
    return (@[ @"title1", @"title2", @"title3" ])[section];

【讨论】:

【参考方案9】:

我更喜欢使用Objective-C++,将文件名从xxx.m更改为xxx.mm,NSArray的初始化将在运行时发生

nodispatch_once,no类方法,写一行就行了:

static NSArray *const a = @[@"a",@"b",@"c"];

【讨论】:

使用该代码会出现错误:“Initializer element is not a compile-time constant”

以上是关于字符串的静态 NSArray - 如何/在哪里初始化视图控制器的主要内容,如果未能解决你的问题,请参考以下文章

如何使用代码初始化静态成员字符数组

如何更改静态链接库中 const 字符串数组的 Visual Studio C++ 初始化序列

如何从另一个数组创建一个对象的两个 valueForKey 元素的 NSArray

NSArray 初始化方法

ObjectiveC: 变量和数据类型:初始化方法外部/静态变量枚举类型typedef类型转换位运算符

从内存的角度来看,具有非持久存储的 NSArray 和 Core Data 是如何工作的?