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

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

  1. 你的举动
  2. 他们的行动
  3. 赢得比赛
  4. 输掉的比赛
  5. 选项

所以我在 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 或某种形式的锁定会更安全。我可以从第一手经验告诉你,否则崩溃错误的可能性是非常真实的。

标签: ios objective-c nsmutablearray initialization nsarray


【解决方案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”
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-02-06
          • 2016-08-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多