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 = ... ; } } 

然后,当然,计算必须由电话完成两次(如果条件为真),但不必写两次。

可能不是最优雅的解决方案,但如果您只想保持简单,这是另一种选择。