Objective-C @available可以保护AND并拥有更多条件

use*_*008 21 objective-c ios availability xcode9

Objective-C 在XCode 9+/LLVM 5+中有一个@available表达式,它允许您将代码块保护到至少某个操作系统版本,这样如果您使用仅在该范围内可用的API,它就不会发出无人看守的可用性警告操作系统版本

问题在于,这种可用性保护是它只有在它是条件的唯一表达时才有效if.如果您在任何其他上下文中使用它,您会收到警告:

@available does not guard availability here; use if (@available) instead
Run Code Online (Sandbox Code Playgroud)

因此,例如,如果您尝试将可用性检查与以下其他条件进行对比,则它不起作用if:

if (@available(iOS 11.0, *) && some_condition) {
  // code to run when on iOS 11+ and some_condition is true
} else {
  // code to run when on older iOS or some_condition is false
}
Run Code Online (Sandbox Code Playgroud)

任何在if块内或在块内使用iOS 11 API的代码some_condition都会产生无人看守的可用性警告,即使可以保证只有在iOS 11+上才能访问这些代码.

我可以把它变成两个嵌套if的,但是else代码必须重复,这很糟糕(特别是如果它有很多代码):

if (@available(iOS 11.0, *)) {
  if (some_condition) {
    // code to run when on iOS 11+ and some_condition is true
  } else {
    // code to run when on older iOS or some_condition is false
  }
} else {
  // code to run when on older iOS or some_condition is false
}
Run Code Online (Sandbox Code Playgroud)

我可以通过将else块代码重构为匿名函数来避免重复,但这需要在else之前定义块if,这使得代码流难以遵循:

void (^elseBlock)(void) = ^{
  // code to run when on older iOS or some_condition is false
};

if (@available(iOS 11.0, *)) {
  if (some_condition) {
    // code to run when on iOS 11+ and some_condition is true
  } else {
    elseBlock();
  }
} else {
  elseBlock();
}
Run Code Online (Sandbox Code Playgroud)

任何人都可以提出更好的解决方案吗?

Rob*_*ier 9

如果在函数中间有复杂的条件代码使流程变得复杂,那么就可以执行常规操作:将其提升到另一个函数中.

- (void)handleThing {
    if (@available(iOS 11.0, *)) {
        if (some_condition) {
            // code to run when on iOS 11+ and some_condition is true
            return;
        }
    }

  // code to run when on older iOS or some_condition is false
}
Run Code Online (Sandbox Code Playgroud)

或者你将检查提升为通用代码(参见Josh Caswell的;它比我最初编写的更好).


Bra*_*don 7

#define SUPPRESS_AVAILABILITY_BEGIN \
    _Pragma("clang diagnostic push") \
    _Pragma("clang diagnostic ignored \"-Wunsupported-availability-guard\"")\
    _Pragma("clang diagnostic ignored \"-Wunguarded-availability-new\"")

#define SUPPRESS_AVAILABILITY_END \
    _Pragma("clang diagnostic pop")

#define AVAILABLE_GUARD(platform, os, future, conditions, codeIfAvailable, codeIfUnavailable) \
    SUPPRESS_AVAILABILITY_BEGIN \
    if (__builtin_available(platform os, future) && conditions) {\
        SUPPRESS_AVAILABILITY_END \
        if (@available(platform os, future)) { \
            codeIfAvailable \
        } \
    } \
    else { \
        SUPPRESS_AVAILABILITY_END \
        codeIfUnavailable \
    }
Run Code Online (Sandbox Code Playgroud)

用法:

AVAILABLE_GUARD(iOS, 11.0, *, true, {
    printf("IS AVAILABLE");
},
{
    printf("NOT AVAILABLE");
});
Run Code Online (Sandbox Code Playgroud)

它通过使用@available作为具有附加可选条件的条件来工作.由于你失去了"守卫"的能力,我压制了无人防守的警告,但我还在那里添加了一个额外的守卫以保护其余的代码..这使得你基本上没有失去任何东西..

你得到了警卫,你得到了警告,你获得了额外的条件..


Jos*_*ell 2

将 AND 封装在一个函数中怎么样?

typedef BOOL (^Predicate)();

BOOL elevenAvailableAnd(Predicate predicate)
{
    if (@available(iOS 11.0, *)) {
        return predicate();
    }
    return NO;
}
Run Code Online (Sandbox Code Playgroud)

那么你只有一个分支:

if (elevenAvailableAnd(^{ return someCondition })) {
    // code to run when on iOS 11+ and some_condition is true
}
else {
    // code to run when on older iOS or some_condition is false
}
Run Code Online (Sandbox Code Playgroud)

或者,如果您愿意,也可以不使用 Block:

BOOL elevenAvailableAnd(BOOL condition)
{
    if (@available(iOS 11.0, *)) {
        return condition;
    }
    return NO;
}
Run Code Online (Sandbox Code Playgroud)

  • 但这样就无法阻止使用仅限 iOS 11 API 的 if 块中的代码中出现不受保护的可用性警告。 (3认同)