ios - Objective-C @available Guard AND с большим количеством условий




xcode9 availability (4)

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


Вы можете сначала сделать else-код и как-то сохранить результат, а затем выполнить 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 variable
         */
        resultOfCalculations  = ... ;
    }
}

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

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


Как насчет упаковки 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;
}

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

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

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





availability