Как использовать nonnull и nullable Objective-C ключевые слова в методе API на основе блоков

Рассмотрим следующий метод

- (void)methodWithArg:(NSString *)arg1 andArg:(NSString *)arg2 completionHandler:(void (^)(NSArray *results, NSError *error))completionHandler;

С помощью новых nonnull и nullable ключевых слов аннотации мы можем обогатить его следующим образом:

- (void)methodWithArg:(nonnull NSString *)arg1 andArg:(nullable NSString *)arg2 completionHandler:(void (^)(NSArray *results, NSError *error))completionHandler;

но мы также получаем это предупреждение:

В указателе отсутствует спецификатор типа nullability (__nonnull или __nullable)

Он ссылается на третий параметр (первый блок).

Документация не содержит примеров того, как указать допустимость параметров блока. Он утверждает дословно

Вы можете использовать незанятые формы с нулевым значением и ненулевым после открытой круглой скобки, если тип является простым объектом или указатель блока.

Я попытался поставить одно из двух ключевых слов для блока (в любой позиции) без всякой удачи. Также попытались использовать префиксные символы подчеркивания (__nonnull и __nullable).

Поэтому мой вопрос: как я могу указать семантику неопределенности для параметров блока?

Ответ 1

Кажется, что это работает

- (void)methodWithArg:(nonnull NSString *)arg1 
  andArg:(nullable NSString *)arg2 completionHandler:(nullable void (^)
  (NSArray * _Nullable results, NSError * _Nonnull error))completionHandler

Вам нужно указать значение nullability как для блока, так и его параметров...

EDIT: Для получения дополнительной информации см. Swift Blog

Ответ 2

Согласно Apple Blog ( "Nullability и Objective-C" ), вы можете использовать

NS_ASSUME_NONNULL_BEGIN и NS_ASSUME_NONNULL_END.

В этих регионах любой простой тип указателя будет считаться nonnull. Затем вы можете просто добавить nullable для объекта с нулевым значением, который, например,

NS_ASSUME_NONNULL_BEGIN

@interface MyClass: NSObject

- (void)methodWithArg:(NSString *)arg1 andArg:(nullable NSString *)arg2 completionHandler:(void (^)(NSArray *results, NSError *error))completionHandler;

@end

NS_ASSUME_NONNULL_END
  • если ошибка NSError **, должна быть NSError * _Nullable * _Nullable
  • Если объект имеет тип id *, лучше использовать id _Nullable * _Nonnull, он зависит (может быть, вы хотите тип _Nullable id * _Nullable).
  • Если тип объекта NSObject *, вам нужно поместить аннотацию после указателя, например NSObject * _Nullable * _Nonnull

Примечание

_Nonnull и _Nullable должны использоваться после указателя или id (Apple делает в примере код AAPLListItem * _Nullable), но без подчеркивания формы nonnull и nullable могут использоваться после открытой круглой скобки.

Однако, в общем случае, гораздо лучший способ написать эти аннотации: в объявлениях методов вы можете использовать не подчеркнутые формы nullable и nonnull сразу после открытия скобки, если тип является простым объектом или указателем блока.

проверьте больше в "Nullability и Objective-C"

Для безопасности существует несколько исключений из этого правила:

  • typedef типы обычно не имеют присущей nullability - они могут легко быть либо нулевым, либо непустым значением в зависимости от контекста. Поэтому типы typedef не считаются nonnull, даже внутри проверенных регионов.
  • Более сложные типы указателей, такие как id *, должны быть явно аннотирован. Например, чтобы указать указатель, не содержащий NULL к ссылке с нулевым объектом, используйте _Nullable id * _Nonnull.
  • Частный тип NSError ** часто используется для возврата ошибок через параметры метода, которые всегда считаются нулевым указателем к нулевому значению NSError.

_Nullable id * _Nonnull можно смутить, id _Nullable * _Nonnull лучше понять.

_Nonnull и _Nullable должны использоваться после указателя или id (Apple делает в примере кода AAPLListItem * _Nullable)

Ответ 3

Чтобы определить завершение в файле заголовка, я сделал это

typedef void (^PublicEventsHandler) (BOOL success, NSArray * _Nullable publicEvents);

Конечно, я согласен с принятым ответом.

Ответ 4

Вы также можете сделать следующее:

- (id __nullable)methodWithArg:(NSString * __nullable)arg1
                        andArg:(NSString * __nonnull)arg2
             completionHandler:(void (^ __nonnull)(NSArray * __nonnull results, NSError * __nullable error))completionHandler;

Это зависит только от того, какой синтаксис вам больше нравится.

Ответ 5

Из яблока блог разработчика: Ядро: _Nullable и _Nonnull

вы можете использовать нечеркнутые формы nullable и nonnull сразу же после открытой круглой скобки, пока тип - простой объект или указатель блока.

Неподчеркнутые формы лучше, чем подчеркнутые, но Вам все равно нужно применять их к каждому типу в заголовке.

Ответ 6

Вот что я использовал для случая NSError **:

-(BOOL) something:(int)number withError:(NSError *__autoreleasing  __nullable * __nullable)error;