Возможно, это не самое элегантное решение, но если вы хотите, чтобы оно было простым, это альтернатива.

tive-C имеет@available выражение в XCode 9+ / LLVM 5+, который позволяет защитить блок кода как минимум до определенной версии ОС, чтобы он не выдавал неохраняемые предупреждения о доступности, если вы используете API, которые доступны только в этой версии ОС.

Проблема заключается в том, что эта защита доступности заключается в том, что она работает, только если она является единственным выражением в условииif, Если вы используете его в любом другом контексте, вы получите предупреждение:

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

Так, например, это не сработает, если вы попытаетесь И проверить доступность с другими условиями в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
}

Любой код, который использует API iOS 11 внутриif блок или в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
}

Я могу избежать дублирования путем рефакторинга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();
}

Может кто-нибудь придумать лучшее решение?

 user10200827 окт. 2017 г., 03:12
@NicolasMiari: нет
 Nicolas Miari27 окт. 2017 г., 03:07
Вам также не нужно проверять противsome_condition также вelse блок изif (@available... ...?
 RobP27 июн. 2018 г., 16:07
Я думаю, что вариант вашего последнего решения является лучшим, используя метод вместо блока, чтобы определение метода могло быть ПОСЛЕ всего этого условного кода. Просто замениelseBlock() с участием[self elseMethod];

Ответы на вопрос(6)

#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
}

Или вы можете обойтись без блока, если вы предпочитаете:

BOOL elevenAvailableAnd(BOOL condition)
{
    if (@available(iOS 11.0, *)) {
        return condition;
    }
    return NO;
}
 Josh Caswell27 окт. 2017 г., 03:32
Ах. Дерьмо. Я думаю, что «подать радар», вероятно, правильный ответ. К сожалению, это не поможет в ближайшее время.
 user10200827 окт. 2017 г., 02:16
Но тогда это не помешает неосторожным предупреждениям о доступности в коде блока if, который использует API-интерфейсы только для iOS 11.

Вы также можете просто использовать флаг:

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
}
 user10200827 окт. 2017 г., 02:15
Но тогда это не помешает неохраняемым предупреждениям о доступности в кодеif блок, который использует API только для iOS 11.

а затем, если необходимо, выполнить if-код. Что-то вроде этого:

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

Тогда, конечно, расчет должен быть сделан дважды по телефону (если условия верны), новы не нужно писать это дважды.

Возможно, это не самое элегантное решение, но если вы хотите, чтобы оно было простым, это альтернатива.

как я пришел к этому, похоже, меняет структуру кода по меньшей мере:

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);

который все еще уродлив.

 Josh Caswell28 окт. 2017 г., 18:58
Я думаю, что вы также можете использоватьgoto в таком случае; было бы намного яснее, как контроль прыгал вокруг.

что всегда делаете, когда у вас есть сложный условный код в середине функции, которая делает поток сложным: вы поднимаете его в другую функцию.

- (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's; это лучше, чем то, как я изначально написал это).

Ваш ответ на вопрос