否定Objective-C中的@available关键字

21

我想只在当前设备的iOS版本低于特定版本时才运行一段代码,如这里所述。苹果提供的代码示例如下:

if (@available(iOS 10.0, *)) {
  // iOS 10.0 and above
} else {
  // below 10.0
}

然而,有些情况下我们只想在当前iOS版本低于特定版本时才运行代码。我假设以下代码可以实现:

However, there are scenarios where one would like to run code only if the current iOS version is below a specific version. I assumed the following code will work:

if (!@available(iOS 10.0, *)) {
  // below 10.0
}

然而,似乎这并不起作用,并且我正在从Xcode收到以下警告:

@available does not guard availability here; use if (@available) instead

这里是添加我看到的诊断工具的 LLVM 提交记录。

有两种可能的解决方法:

  1. 使用 if-else 变体,而不向 if 块添加任何代码(不太优雅)。
  2. 继续使用旧的方法,例如 -[NSProcessInfo isOperatingSystemAtLeastVersion:]

是否有其他可以使用 @available 的预期方式我未注意到的?


1
我已经阅读了LLVM文章,其中指出您不能在if语句中与任何其他条件或指令一起使用@available。因此,基本上我能想到的唯一方法是拥有一个空的if体,但在else块中执行操作。这似乎是我唯一可能的方式。 - cramopy
然而,看起来这并不起作用,我从Xcode收到以下警告:仅仅因为有一个警告并不意味着它不起作用。该警告表示它没有保护可用性,但你只是将其用作版本检查,而不是用于保护可用性。 - user102008
3个回答

11

@available 的想法是,你想使用仅在某些系统上可用的 API。对于其他系统,该功能可能会在您的应用中丢失,或者您提供替代功能。在仅需要低于某个OS版本的代码之外的情况下使用它的正确方法是:

@available(iOS 10, *)

// your code using the latest API

@available(iOS, unavailable)

// your code for older versions of iOS

if (@available(iOS 10.0, *)) {
    // Happens automatically on on iOS 10 and beyond
} else {
    someOtherCode();
}
这是由于编译器有时需要执行一些额外的操作来处理由@available保护的代码,因此需要清楚地识别这种情况。实际上,编译器会明确搜索


if (@available(...)) {

只允许在空格和换行方面进行变化。是的,你可能会争辩说一个单独的感叹号(!)并不复杂,但是你会从哪里划定界限呢?那么以下情况呢:

if ((todayIsTuesday() && @available(iOS 9.0, *)) 
    || (self.theWeatherIsNice && !@available(iOS 11.0, *)) {
因此,只允许使用简单语句,这些语句只强制编译器将代码分成两个部分,并且始终只有一个部分肯定会运行:一个用于列出的操作系统,另一个用于其余部分。当然,“其余部分”可以再次细分,允许使用else if。如果缺少else部分,则只能自动生成else部分,因此当您编写以下内容时:
if (@available(...)) {
    someCode();
} // There is no else

编译器也很高兴,因为这与之前的代码是一样的。

if (@available(...)) {
     someCode();
} else {
    // Nothing to do here
}

1
接受详细的答案。我同意你的推理,尽管我认为否定的这种特殊情况需要特别处理。 - StatusReport
1
@StatusReport 也许你应该向Clang提交一个增强请求,而不是在苹果门户网站上提交,而是直接在LLVM门户网站上提交。他们要么会实现它,要么会提供一个很好的理由,说明他们不想或者技术上无法以那种方式实现。LLVM开发人员似乎非常反应迅速,在那里之前也提交了问题,它们都得到了解决。 - Mecki

1
你可以定义自己的自定义宏,以便在整个应用程序中使用。例如:-
#define isIOS11() ([[UIDevice currentDevice].systemVersion doubleValue]>= 11.0 && [[UIDevice currentDevice].systemVersion doubleValue] < 12.0)

or

#define SinceIOS9_2 ([[UIDevice currentDevice].systemVersion doubleValue]>= 4.2 && [[UIDevice currentDevice].systemVersion doubleValue] < 9.2)

请看以下使用方式:
if (isIOS11()) {
    // Do something for iOS 11 
} else {
    // Do something iOS Versions below 11.0
}

请告诉我这是否对您有效。

这很不错。我想有一天我会尝试一下。谢谢! - Akaino
1
这不会抑制Xcode 9中新的“未保护可用性”警告。 - StatusReport

1
if (@available ...) {
   ...
} else {
   ...
}

只允许使用if语句的形式。这是必须的,因为在if部分和else部分应用不同的规则。在if部分,可以调用一个SDK中可用的方法,在else部分,可以调用另一个SDK中的方法。同一调用在一个分支中可能合法,在另一个分支中可能非法,或者在一个分支中已弃用,在另一个分支中未弃用。


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