Objective-C @available可以在更多条件下保持AND
Objective-C在XCode 9+ / LLVM 5+中有一个@available
表达式 ,允许您将代码块保护到至少某个操作系统版本,这样如果您使用仅可用的API,它就不会发出无人看守的可用性警告在该操作系统版本上。
问题在于,这种可用性保护只有在它是if的条件下的唯一表达时才有效。 如果您在任何其他环境中使用它,您会收到警告:
@available does not guard availability here; use if (@available) instead
因此,例如,如果您尝试与if
其他条件进行AND可用性检查,则它不起作用:
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 }
在if
块或some_condition
中使用iOS 11 API的任何代码仍然会生成无人看守的可用性警告,即使保证只有在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 }
我可以通过将else
块代码重构为匿名函数来避免重复,但这需要在else
之前定义else
块,这使得代码流难以遵循:
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(); }
任何人都可以提出更好的解决方案吗?
当你在一个使流程变得复杂的函数中有复杂的条件代码时,你会做你总是做的事情:你将它提升到另一个函数中。
- (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 }
或者你将检查提升为通用代码(参见Josh Caswell的;它比我最初写的更好)。
#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 \ }
用法:
AVAILABLE_GUARD(iOS, 11.0, *, true, { printf("IS AVAILABLE"); }, { printf("NOT AVAILABLE"); });
它的工作原理是使用@available作为具有附加可选条件的条件。 由于你失去了“守卫”的能力,我压制了无人防守的警告,但我还在那里添加了一个额外的守卫以保护其余的代码..这使得你基本上没有失去任何东西..
你得到了警卫,你得到了警告,你获得了额外的条件..
如何将AND包装在函数中?
typedef BOOL (^Predicate)(); BOOL elevenAvailableAnd(Predicate predicate) { if (@available(iOS 11.0, *)) { return predicate(); } return NO; }
那你只有一个分支:
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 }
或者,如果您愿意,可以不使用Block:
BOOL elevenAvailableAnd(BOOL condition) { if (@available(iOS 11.0, *)) { return condition; } return NO; }
你也可以简单地使用一个标志:
BOOL doit = FALSE; if (@available(iOS 11.0, *)) { if (some_condition) { doit = TRUE; } } if (doit) { // 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 }
我提出的方式似乎改变了代码的布局最少:
do { if (@available(iOS 11.0, *)) { if (some_condition) { // code to run when on iOS 11+ and some_condition is true break; } } // code to run when on older iOS or some_condition is false } while (0);
这仍然是丑陋的。
你也可以这样做:
/** first make default calculations */ id resultOfCalculations = ... ; if (@available(iOS 11.0, *)) { if (some_condition) { /** code to run when on iOS 11+ and some_condition is true redo calculations and overwrite variable */ resultOfCalculations = ... ; } }
然后,当然,计算必须由电话完成两次(如果条件为真),但你不必写两次。
可能不是最优雅的解决方案,但如果您只想保持简单,这是另一种选择。