簡體   English   中英

Objective-C 塊的全局默認值

[英]Global default value for 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. 在單獨的文件中定義默認的常量塊並從我的實現 class 中引用的好方法是什么?

在 Objective-C(或更准確地說,在 C 中)中,當您有這樣的全局時,

  • 實際的全局變量應該在源文件中( .m Objective-C 文件,或.c在 C 的情況下)。 在這里你可以包括初始化。 但不應使用extern限定符。

  • .h應該有extern聲明(沒有初始化)以將此全局公開給其他編譯單元。

讓我們考慮一個更簡單的示例(將 Objective-C 塊的句法噪聲排除在等式之外)。

因此,我們在.m文件中聲明一個全局...

//  Foo.m

#import "Foo.h"

NSInteger baz = 42;

@implementation Foo

@end

...我們在其.h header 中公開該全局:

//  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. 在單獨的文件中定義默認的常量塊並從我的實現 class 中引用的好方法是什么?

雖然您可以使用上面概述的正確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;
}

當然,這引出了一個問題,即如果塊是常量,為什么還要使用它。 塊的價值是調用者可以提供一個代碼塊,被調用的例程將利用它。 例如,如果我們想調用一些 Objective-C 方法,我們可能只創建一個 class 方法:

// 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

這是一個可以在任何地方調用的例程,無需使用全局變量,不需要實例化有問題的 object,但該方法的命名空間很好。

刪除 .m 文件中的extern “extern”的意思是“這是在其他地方定義的”,但你在這里定義它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM