Objective-C块的全局默认值



我有一个初始化器,它接受一个block。我还想要一个方便的初始化器,它不需要块,而是使用块的全局默认实例:

ABCDef.m:

#import "ABCDefaultReader.h"
@implementation ABCDef {
NSString *(^_reader)(NSString *name);
}
- (instancetype)initWithReader:(NSString *(^)(NSString *name))reader {
self = [super init];
if (self) {
_reader = reader;
}
return self;
}
- (instancetype)init {
// ABCDefaultReader imported from ABCDefaultReader.h
return [self initWithReader:ABCDefaultReader];
}

ABCDefaultReader.h:

extern NSString *(^ABCDefaultReader)(NSString *name);

ABCDefaultReader.m:

#import "ABCDefaultReader.h"
extern NSString *(^ABCDefaultReader)(NSString *name) = ^NSString *(NSString *name) {
// ...
}

这无法构建,因为'extern' variable has an initializer(我使用-Werror):

ABCDefaultReader.m:3:36: error: 'extern' variable has an initializer [-Werror,-Wextern-initializer]
extern NSString *(^ABCDefaultReader)(NSString *name) = ^NSString *(NSString *name) {
^
1 error generated.
  1. 为什么有一个初始化器是一个问题?
  2. 在一个单独的文件中定义一个默认的常量块,并从我的实现类中引用,这是什么好方法?

在Objective-C中(或者更准确地说,在C中),当你有一个这样的全局变量时,

  • 实际的全局变量应该在源文件中(.mObjective-C文件,或.c在C的情况下)。在这里你可以包括初始化。但是不能使用extern限定符。

  • .h应该有extern声明(没有初始化),将这个全局变量暴露给其他编译单元。

让我们考虑一个更简单的例子(将Objective-C块的语法噪声排除在方程之外)。

因此,我们在.m文件中声明了一个全局变量…

//  Foo.m
#import "Foo.h"
NSInteger baz = 42;
@implementation Foo
@end

…我们在.h头文件中公开了这个全局变量:

//  Foo.h
#import <Foundation/Foundation.h>
extern NSInteger baz;
@interface Foo : NSObject
@end

现在我们可以从其他文件访问这个全局变量:

//  Bar.m
#import "Bar.h"
#import "Foo.h"
@implementation Bar
- (void)qux {
NSLog(@"%ld", (long)baz); // 42
}
@end

你问:

  1. 为什么有一个初始化器是一个问题?

extern有效地表示"在其他地方有一个全局实现"。在那里初始化它没有意义。

  1. 在单独的文件中定义一个默认的常量块,并从我的实现类中引用,这是什么好方法?

虽然您可以使用正确的extern模式,如上所述,我建议不要使用全局变量。我可能为这个块设置了class属性。或者我可能为这个块拥有一个实例属性,然后把它放在某个共享实例中(在需要时注入的共享实例,或者在非常狭窄的情况下,可能是一个单例实例)。在没有更多背景的情况下,很难说你的具体情况。

但是我们通常会避免污染全局命名空间。


顺便说一下,您已经将此块描述为"常量"。如果你真的不是指"常量",那么忽略以下内容,但如果是这样,请继续阅读:

因为您已经定义了这个全局变量,所以它是可变的,可以在代码中的任何地方替换。如果它确实是常量,则应该包含const限定符:

// .h
extern NSString *(^const ABCDefaultReader)(NSString *);
// .m
extern NSString *(^const ABCDefaultReader)(NSString *) = ^(NSString *string) {
// do something with `string`
NSString *result = ...
return result;
}

当然,这就引出了一个问题:如果它是常量,为什么要使用block呢?block的价值在于调用者可以提供被调用例程将利用的代码块。例如,如果我们想调用某个Objective-C方法,我们可能只需要创建一个类方法:

// Foo.h
@interface Foo: NSObject
+ (NSString *)bar:(NSString *)input;
@end

// Foo.m
@implementation Foo
+ (NSString *)bar:(NSString *)input {
NSString *output = ...
return output;
}
@end

然后你可以这样称呼它:

// Baz.m
@implementation Baz
- (void)someRoutine {
NSString *result = [Foo bar:@"baz"];
// do something with `result`
}
@end

这是一个可以在任何地方调用的例程,不需要使用全局变量,也不需要实例化所讨论的对象,但是该方法的命名空间很好。

删除.m文件中的extern。"extern"意思是"这是在别处定义的"但是你在这里定义了它

最新更新