【问题标题】:Xcode preprocessor macro to check if Base SDK >= iOS 7.0Xcode 预处理器宏来检查 Base SDK >= iOS 7.0
【发布时间】:2013-11-05 13:20:17
【问题描述】:

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

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

我问这个的原因是我目前正在将应用程序从 iOS 6 转换到 iOS 7。有很多事情需要调整,我目前仍然想以 iOS 6.1 为基础编译我的应用程序SDK(以及开发目标 iOS 6.0),但已经想添加一些我在使用 iOS 7 SDK 编译时需要的代码,但如果基础 SDK 是 iOS 6.1,则无法编译。

例子:

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

上面的这段代码不能用 iOS 6.1 基础 SDK 编译,因为它抱怨 setSeparatorInset 没有为 UITableView 定义。因此,我想将这段代码包含在预处理器指令中,有条件地基于基础 SDK。

【问题讨论】:

  • 在迁移到 iOS 7 SDK 时,为什么还要使用 iOS 6 SDK 编译应用程序?
  • 因为有很多我需要解决的问题我还没有完成(一些 UI 工件在新的 iOS7 SDK 中看起来很奇怪)但我现在需要编译一个快速版本来解决一个紧急的错误。

标签: ios xcode ios7


【解决方案1】:

您应该阅读 Apple 的 SDK Compatibility Guide,其中解释了所有这些技术。

他们特别推荐使用__IPHONE_OS_VERSION_MIN_REQUIRED 宏来测试您项目的部署目标(支持的最低版本),并针对您的情况使用__IPHONE_OS_VERSION_MAX_ALLOWED 宏测试用于编译的 Base 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(即使 Base SDK 是 iOS 7)。我需要将开发目标设置为 iOS 7 才能使条件正常工作。
  • 我一直使用__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 版本。
  • 另外,您是否阅读了指向我的链接的文档?他们解释了在什么情况下你应该使用__IPHONE_OS_VERSION_MAX_ALLOWED,在什么情况下你不需要,以及使用什么其他正确的技术来实现这种东西。
  • 您在 Xcode5 中安装了 SDK6?! (可能是通过将 Xcode.app 包中的目录从旧的 Xcode4 复制到新的 Xcode5 我想?)这可能是这种奇怪行为的根源。如果您需要使用 SDK6 构建,我建议您保留一个 Xcode4.app,并在需要使用 SDK7 编译时拥有 Xcode5.app(并删除您在 Xcode5 中复制的 SDK6)。这就是我所做的(我的开发 Mac 上有 Xcode4.app 和 Xcode5.app),__IPHONE_OS_VERSION_MAX_ALLOWED 没有问题。
  • @AliSoftware 哇,我遇到了同样的问题:当我从 Xcode 包中删除 6.1 SDK 时,一切正常。
【解决方案2】:

是的,您可以使用__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没有定义,所以它不适合我的目的。
  • 是的,这是正确的,因此当 __IPHONE_7_0 未定义时,#ifdef#endif 之间的代码不会被编译。如果您使用 iOS 7 SDK 编译相同的代码,则包含该代码。
  • 事实并非如此。如果我将基础 SDK 设置为 iOS 7,则这段代码不会被编译(要编译,我还需要将开发目标设置为 iOS 7,这不是我想要的)。我正在寻找一个预处理器条件,如果基础 SDK 是 iOS 7 或更高版本,则无论开发目标的价值如何(实际上我想保留在 iOS 6)
  • 奇怪,我也在做同样的事情,对我来说它正在工作。虽然我在为 iOS 6 SDK 进行任何编译时使用的是 Xcode 4.6。
  • 不幸的是,这并不总是正确的。目前 XCode 7.3.1 中的 SDK 适用于 iOS 9,但标头具有 __IPHONE_10_0 的#define。
【解决方案3】:

根据 Apple Doc,您应该使用 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 以及带有 iOS6 的 iOS7 SDK 进行编译目标。

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

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

【讨论】:

  • 如果 isIOS6 是指 6.1 或更早版本,则应该是
  • @Jano 感谢您注意到这一点。我已经修复了这两个定义。
  • 您能否避免使用 floor 进行大于比较,然后将 >= 与 7 进行比较。这样您就无需依赖 7 跟在 6.1 之间且中间没有任何内容的事实
  • @graveley 您不能这样做,因为在 iOS6 SDK 中您没有 NSFoundationVersionNumber_iOS_7 定义。
【解决方案4】:

另外,你可以使用这个宏

#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:(v) options:NSNumericSearch] != NSOrderedAscending)

例如if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"6.1")) {[self doSomething];} else {[self doSomethingElse];}

【讨论】:

  • 这是一个很好的运行时检查,但问题是关于编译时 SDK 版本。
  • 不要检查系统版本,而是检查 SDK 版本,例如:if (floor(NSFoundationVersionNumber) &lt;= NSFoundationVersionNumber_iOS_6_1) for iOS 6 或更低版本。正如苹果在其中提到的那样iOS 7 UI Transition Guide
【解决方案5】:

您不应该测试 sdk,而是测试方法/类恕我直言的可用性。所以根本不用预编译

【讨论】:

  • 但有时,尤其是在库中,您希望能够检测 SDK 以包含一些特性或新方法。您始终可以通过检测方法或类来做到这一点。
  • “你可以”还是“你不能”?你能告诉我这样的功能吗?
  • MIND:我确实使用了这里提到的宏,我完全明白每个“规则”都有一个例外——这就是为什么我没有拒绝你的投票:D(只是开玩笑!)
  • 有时您必须在预处理器级别测试 SDK,因为从旧 SDK 调用新方法时甚至无法编译。
  • 好的,你必须检查声明。好的...不是这里提到的电话,但是好的:)
猜你喜欢
  • 1970-01-01
  • 2011-12-11
  • 2010-09-19
  • 1970-01-01
  • 2017-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多