我在Objective-C头文件中定义了一个简单的宏,然后通过项目桥接头将该头文件导入Swift。我可以在Swift中将此宏用作常量,但是当我使用它进行条件编译时,它将无法正常工作。
我在Xcode 10.2.1中创建了一个简单项目,并添加了一些代码来重现它。 在ViewController.h
#define TEST_FLAG 1
@interface ViewController : UIViewController
@end
在ViewController.m
#import "testMacro-Swift.h"
- (void)viewDidLoad {
[super viewDidLoad];
SwiftClass *s = [[SwiftClass alloc] init];
[s printMSG];
#if TEST_FLAG
NSLog(@"Objc works.");
#endif
}
在testMacro-Bridging-Header.h
#import "ViewController.h"
SwiftFile
@objc class SwiftClass: NSObject {
@objc func printMSG() {
print("Macro \(TEST_FLAG)")
#if TEST_FLAG
print("compiled XXXxXXXXX")
#endif
}
}
控制台输出
Macro 1
2019-07-03 14:38:07.370231-0700 testMacro[71724:11911063] Objc works.
我希望compiled XXXxXXXXX
之后会打印Macro 1
,但不会。
我很好奇为什么会这样。 我的项目混合了objc和swift。我不想迅速声明相同的标志。
答案 0 :(得分:2)
根据Apple这篇https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/using_imported_c_macros_in_swift文章,简单的C(和Objective-C)宏作为全局常量导入了Swift。
行的输出对此进行了证明。print("Macro \(TEST_FLAG)")
摘要
#if TEST_FLAG
print("compiled XXXxXXXXX")
#endif
使用另一个TEST_FLAG
,它是Swift预处理器标志。您可以在构建设置->活动编译条件下将其定义为TEST_FLAG
,或者在构建设置->其他Swift标志下将其定义为-DTEST_FLAG
。
上面解释了为什么会发生这种情况。我想不出一种简单的方法来避免为Xcode中的Objective-C和Swift预处理器分别定义相同的标志。如果您只想控制是否根据TEST_FLAG
执行了某些Swift代码,则可以执行以下操作:
if TEST_FLAG != 0 {
print("compiled XXXxXXXXX")
}
但是,如果您想控制代码的编译,则可能必须为Objective-C和Swift使用单独的TEST_FLAG
,并确保它们一致。为了使它们一致,您可以在TEST_FLAG
中设置Objective-C代码使用的Other C Flags
,从而可以为不同的SDK,体系结构和构建类型定义不同的标志(发布/调试) 。主动编译条件具有相同的灵活性。
另一个促进(Objective-)C和Swift编译器标志一致性的技巧是创建一个新的用户定义的构建设置:单击“构建设置”下搜索框左侧的+
。
说,将其命名为COMMON_TEST_FLAG
,并将其值设置为TEST_FLAG
。然后将-D$(COMMON_TEST_FLAG)
添加到其他C标志和其他Swift标志。现在,在构建代码时,TEST_FLAG
将在目标中的Objective-C和Swift代码中进行定义。如果您不想定义它,只需将COMMON_TEST_FLAG
的值更改为其他值即可。不过,有几点需要注意:
COMMON_TEST_FLAG
为空:这将导致另一个
标志只是-D,导致生成错误。 COMMON_TEST_FLAG
的值与
宏在其他地方定义。答案 1 :(得分:1)
Swift的预处理器(有意地)比C受到更多限制。宏带有严重的缺点。
它们定义了不是在每个构建目标中都处于活动状态的代码区域。因此,您的测试不会遍历每个分支(甚至不会编译每个分支)。这很快成为维护灾难。对于n
个唯一标志,可能有2 ^ n possible sets of values and thus,
2 ^ n个构建。您是否必须测试每个?也许,也许不是,但是,即使只是对要测试的内容进行推理也不容易。
它们可能会纠结在一起,变得异常复杂,尤其是使用嵌套块时。
通常,请尝试使用主要编程语言(C,ObjC,Swift)表达尽可能多的代码,并仅在有充分理由的情况下才使用宏,例如:
#define max(a, b) ((a) < (b) ? (b) : (a))
)。尽管这种情况很少见。if #available(...)
。您的示例不满足以上任何条件。这是增加的重复,它对性能没有要求,并且没有执行常规Swift代码无法完成的工作。
一种更好的方法(在Swift和Objective C中)是创建一个记录器,该记录器在调试和发行版本之间使用不同的配置进行初始化。您的viewDidLoad
方法不应该自己考虑是否设置了TEST_FLAG
。视图控制器应该控制视图,而不是决定应该记录什么内容。它应该只调用记录器以发送要发送的任何日志消息,然后由记录器决定如何记录这些消息(到输出流,文件,内存循环缓冲区,数据库,流到Analytics API,请忽略它们,等等。