Xcode 预处理宏,用于检查基础 SDK 是否大于等于 iOS 7.0。

33

是否有预处理宏仅在基础SDK为7.0或更高版本时编译代码的某些部分? “__IPHONE_7_0”定义的常量似乎与iOS开发目标(而不是基础SDK)相关联。

我正在使用安装了iOS 7和iOS 6.1的XCode 5。

我提出这个问题的原因是,我目前正在将一个应用程序从iOS 6转换到iOS 7。有许多需要调整的地方,我当前仍想将我的应用程序编译为基础SDK为iOS 6.1(并具有开发目标为iOS 6.0),但已经想添加一些仅在使用iOS 7 SDK编译时才需的代码,但如果基础SDK是iOS 6.1则不编译。

例如:

if ([_tableView respondsToSelector:@selector(setSeparatorInset:)]) {
    [_tableView setSeparatorInset:UIEdgeInsetsZero];
}

这段代码在使用iOS 6.1基础SDK时无法编译,因为它抱怨UITableView没有定义setSeparatorInset。因此,我想在预处理器指令中包含此代码片段,有条件地基于基本SDK。


你为什么还在使用iOS 6 SDK编译应用程序,而不是迁移到iOS 7 SDK呢? - rckoenes
1
因为我还有一些问题需要解决(一些UI工件在新的iOS7 SDK下看起来很奇怪),但是现在我需要编译一个快速版本来解决一个紧急的错误。 - quentinadam
5个回答

45
你应该阅读苹果公司的 SDK兼容性指南,其中解释了所有这些技术。
特别是,他们建议使用 __IPHONE_OS_VERSION_MIN_REQUIRED 宏测试您项目的 部署目标(支持的最低版本),并针对您的情况使用 __IPHONE_OS_VERSION_MAX_ALLOWED 宏来测试编译时使用的基础SDK。


例如:

#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000
// Only COMPILE this if compiled against BaseSDK iOS7.0 or greater
if ([_tableView respondsToSelector:@selector(setSeparatorInset:)]) {
   // Even when compiled with BaseSDK 7, only EXECUTE that if the user uses an
   // OS that support this method (namely if the user is running iOS7 or later,
   // but not for users running iOS6).
   [_tableView setSeparatorInset:UIEdgeInsetsZero];
}
#endif

重要提示:在比较时应该使用数字常量,例如,如果你测试#if __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_7_0,在使用SDK 6时它将不起作用,因为__IPHONE_7_0未定义,在这种情况下会被评估为0,因此你的条件将无法按预期工作。


谢谢,我以为那会起作用,但是如果开发目标是iOS 6,那么我得到的__IPHONE_OS_VERSION_MAX_ALLOWED = 60100(即使基本SDK是iOS 7)。我需要将开发目标设置为iOS 7才能使条件起作用。 - quentinadam
我现在已经有一段时间一直使用__IPHONE_OS_VERSION_MAX_ALLOWED,即使是在部署在GitHub上并被许多人使用的公共项目中——有些人仍在使用Xcode4/SDK6,有些人使用Xcode5/SDK7,所有这些都有各种不同的部署目标配置——在我的所有经验中,当使用Xcode5/SDK7构建时,__IPHONE_OS_VERSION_MAX_ALLOWED始终为70000,而__IPHONE_OS_VERSION_MIN_REQUIRED则取决于部署目标。当然,在任何情况下,我总是在构建设置中使用“最新的SDK”(您永远不应更改它),只需更改所使用的Xcode版本即可。 - AliSoftware
我已经阅读了提供的链接,确实很惊讶于以下设置中__IPHONE_OS_VERSION_MAX_ALLOWED返回__IPHONE_6_1:XCode 5与安装了iOS SDK 7和iOS SDK 6.1,开发目标为iOS 6.0,基本SDK为iOS 7。 - quentinadam
2
你在Xcode5里安装了SDK6?(我猜测是通过将旧的Xcode4中的目录复制到新的Xcode5中来实现的?)这可能是这种奇怪行为的根源。我建议你保留一个Xcode4.app,如果你需要使用SDK6进行构建,并且当你需要使用SDK7进行编译时,使用Xcode5.app(并删除你复制到Xcode5中的SDK6)。这就是我所做的(我在我的开发Mac上有Xcode4.app和Xcode5.app),并且没有出现__IPHONE_OS_VERSION_MAX_ALLOWED的问题。 - AliSoftware
1
@AliSoftware 哇,我也有同样的问题:当我从Xcode软件包中删除6.1 SDK时,一切都正常工作了。 - paulrehkugler
显示剩余2条评论

20

是的,您可以使用__IPHONE_7_0定义:

#ifdef __IPHONE_7_0
    if ([_tableView respondsToSelector:@selector(setSeparatorInset:)]) {
        [_tableView setSeparatorInset:UIEdgeInsetsZero];
    }
#endif

已经尝试过了,但如果开发目标是iOS 6.1(但基础SDK是iOS 7),那么__IPHONE_7_0未定义,因此不适合我的目的。 - quentinadam
1
是的,这是正确的,因此当 __IPHONE_7_0 未定义时,在 #ifdef#endif 之间的代码不会被编译。如果使用 iOS 7 SDK 编译相同的代码,则该代码将被包含在内。 - rckoenes
2
那不是这样的。如果我将基础SDK设置为iOS 7,则该代码段不会被编译(为了使其编译,我还需要将开发目标设置为iOS 7,这不是我想要的)。我正在寻找一个预处理器条件,如果基础SDK为iOS 7或更高版本,则编译代码,而不考虑开发目标的值(实际上,我希望将其保留在iOS 6)。 - quentinadam
奇怪,我正在做同样的事情,对我来说它是有效的。尽管在使用iOS 6 SDK进行编译时,我正在使用Xcode 4.6。 - rckoenes
不幸的是,这并不总是成立的。现在XCode 7.3.1中的SDK适用于iOS 9,但头文件中有一个#define为__IPHONE_10_0。 - John Stephen

13
根据苹果文档,您应该使用NSFoundationVersionNumber来区分iOS7和其他版本。您可以使用以下宏来使其更简单:

根据 Apple 文档,您应该使用 NSFoundationVersionNumber 来区分 iOS 7 和其他版本。您可以使用以下宏使其更简单:


根据 Apple 文档,您应该使用 NSFoundationVersionNumber 来区分 iOS 7 和其他版本。您可以使用下列宏来使此过程更加简单:
#define isIOS6 floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1 
#define isIOS7 floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1

然后稍后在代码中执行

#ifdef __IPHONE_7_0
    if (isIOS7) {
       // Do additional stuff for iOS 7.
    } 
#endif

是的,您应该在编译时(使用#ifdef)和运行时(使用isIOS7)都进行检查,这样您就可以使用iOS6 SDK、iOS7 SDK以及将iOS7 SDK与iOS6目标一起使用。

哦!请记住,您不能使用if (!isIOS7),而必须使用if (isIOS6)

https://developer.apple.com/library/ios/documentation/userexperience/conceptual/transitionguide/


如果你指的是iOS6的版本号为6.1或更早,则应该使用<=。 - Jano
@Jano 感谢你注意到了这一点。我已经修正了两个定义。 - Mojtaba
你能避免使用floor函数进行大于比较,然后将>=与7进行比较吗?这样你就不需要依赖于7跟随6.1且中间没有其他数字的事实。 - graveley
@graveley,你不能这样做,因为在iOS6 SDK中没有NSFoundationVersionNumber_iOS_7的定义。 - Mojtaba

3
此外,您可以使用此宏。
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:(v) options:NSNumericSearch] != NSOrderedAscending)

例如:如果 (系统版本大于或等于6.1) {执行某些操作;} else {执行其他操作;}


2
这是一个很好的运行时检查,但问题是关于编译时SDK版本的。 - Tricertops
不要检查系统版本,而是检查SDK版本,例如:if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1) 适用于iOS 6或更低版本。正如苹果在其iOS 7 UI Transition Guide中所提到的那样。 - rckoenes

1

我认为你不应该测试sdk,而是应该测试方法/类的可用性。因此,根本不需要预编译。


1
但有时,特别是在库中,您希望能够检测SDK以包含一些功能或新方法。这可以通过检测方法或类来实现。 - rckoenes
“你能”还是“你不能”?你能告诉我这样一个特性吗? - Daij-Djan
注意:我确实使用这里提到的宏,我完全理解每个“规则”都有例外的道理——这就是为什么我没有给你投反对票的原因:D(只是开玩笑!) - Daij-Djan
2
有时候你需要在预处理器级别上测试SDK,因为从旧的SDK调用新方法时,它们甚至无法编译。 - Tricertops
好的,你需要检查声明。好的...不是针对此处提到的调用,但没问题 :) - Daij-Djan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接