字符串的静态 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 ...
并在init
或awakeFromNib
或任何其他方法(如viewDidLoad
、applicationDidFinishLaunching
)中任意定义。
- (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