Objective-Cには、XCode 9+/LLVM 5+で @available
式 があり、コードのブロックを少なくとも特定のOSバージョンにガードして、ガードなしで出力しないようにすることができますそのOSバージョンでのみ使用可能な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以降でのみ到達できることが保証されている場合でも、無防備な可用性警告を生成します。
2つのネストされたif
sに変換できますが、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
の前に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();
}
誰もがより良い解決策を思い付くことができますか?
フローを複雑にする関数の途中に複雑な条件コードがある場合は、常に行うことを実行します。それを別の関数に巻き上げます。
- (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を追加のオプション条件とともに条件として使用することにより機能します。あなたが「ガード」する能力を失うので、私はガードされていない警告を抑制しましたが、コードの残りをガードするためにそこに余分なガードも追加しました。
あなたは警備をし、あなたは警告を失い、あなたは余分な条件を手に入れます。
定義済み
#define AT_AVAILABLE(...) \
_Pragma("clang diagnostic Push") \
_Pragma("clang diagnostic ignored \"-Wunsupported-availability-guard\"") \
_Pragma("clang diagnostic ignored \"-Wunguarded-availability-new\"") \
__builtin_available(__VA_ARGS__) \
_Pragma("clang diagnostic pop")
使用法:
if (AT_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
}
これをPCHファイルにインポートします
#pragma clang diagnostic ignored "-Wunsupported-availability-guard"
#pragma clang diagnostic ignored "-Wunguarded-availability-new"
使用法:
if (AT_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
}
ANDを関数でラップするのはどうですか?
typedef BOOL (^Predicate)();
BOOL elevenAvailableAnd(Predicate predicate)
{
if (@available(iOS 11.0, *)) {
return predicate();
}
return NO;
}
次に、1つのブランチのみがあります。
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
}
または、必要に応じてブロックなしで行うこともできます。
BOOL elevenAvailableAnd(BOOL condition)
{
if (@available(iOS 11.0, *)) {
return condition;
}
return NO;
}
最初にelse-codeを実行して結果を何らかの方法で保存し、必要に応じてif-codeを実行できます。このようなもの:
/**
first make default calculations, the 'else-code'
*/
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 object
*/
resultOfCalculations = ... ;
}
}
その後、もちろん、計算は電話で2回行われなければなりません(条件が真の場合)。しかし、yoは2回書く必要はありません。
最もエレガントなソリューションではないかもしれませんが、シンプルに保ちたい場合は、これが代替手段です。
私が思いついた方法は、コードのレイアウトをほとんど変更しないようです:
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);
まだいです。
単にフラグを使用することもできます:
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
}