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

106

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

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

Ключевые слова new nonnullи nullable annotation можно дополнить следующим образом:

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

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

В указателе отсутствует спецификатор типа допускающего значение NULL (__nonnull или __nullable)

Относится к третьему параметру (блочному).

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

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

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

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

Альбертодебортоли
источник

Ответы:

128

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

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

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

РЕДАКТИРОВАТЬ: для получения дополнительной информации см. Блог Swift.

Фабио Ритровато
источник
Как это работает с NSError **шрифтом? Кажется, я не могу сделать компилятор счастливым.
duhanebel
3
Согласно блогу The particular type NSError ** is so often used to return errors via method parameters that it is always assumed to be a nullable pointer to a nullable NSError reference.
Swift
@duhanebel Ответ дается в stackoverflow.com/questions/33198597/… : (NSError * _Nullable * _Nullable) error
Elise van Looij
33

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

NS_ASSUME_NONNULL_BEGINи NS_ASSUME_NONNULL_END.

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

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сразу после открытой круглой скобки, если тип является простым указателем на объект или блок.

дополнительные сведения см. в разделе «Возможность обнуления и цель-C»

В целях безопасности из этого правила есть несколько исключений:

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

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

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

likid1412
источник
Для слабого свойства применяется _Nullable.
Dongjin Suh
3

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

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

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

OlDor
источник
2

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

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

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

Кришна Кумар
источник
0

Из блога разработчиков Apple : The Core: _Nullable и _Nonnull

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

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

Параг Бафна
источник
Да, но
Поль Брюно
-2

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

-(BOOL) something:(int)number withError:(NSError *__autoreleasing  __nullable * __nullable)error;
Кевин
источник
1
Как сказала Apple , для NSError **вам не нужно указывать его допустимость значения NULL.
DawnSong