Cómo usar palabras clave Objective-C no nulas y anulables en el método API basado en bloques

105

Considere el siguiente método

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

Con las palabras clave nuevas nonnully de nullable anotación podemos enriquecerlo de la siguiente manera:

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

pero también recibimos esta advertencia:

Al puntero le falta un especificador de tipo de nulabilidad (__nonnull o __nullable)

Se refiere al tercer parámetro (el bloque uno).

La documentación no cubre con ejemplos cómo especificar la nulabilidad de los parámetros de bloque. Dice textualmente

Puede utilizar los formularios no subrayados que aceptan nulos y no nulos inmediatamente después de un paréntesis abierto, siempre que el tipo sea un objeto simple o un puntero de bloque.

Intenté poner una de las dos palabras clave para el bloque (en cualquier posición) sin suerte. También probé las variantes con prefijo de subrayado ( __nonnully __nullable).

Por lo tanto, mi pregunta es: ¿cómo puedo especificar la semántica de nulabilidad para los parámetros de bloque?

albertodebortoli
fuente

Respuestas:

128

Esto parece estar funcionando

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

Debe especificar la nulabilidad tanto para el bloque como para sus parámetros ...

EDITAR: para obtener más información, consulte el Blog de Swift

Fabio Ritrovato
fuente
¿Cómo funciona eso con el NSError **tipo? Parece que no puedo hacer feliz al compilador.
duhanebel
3
Según el blog de 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 La respuesta se da en stackoverflow.com/questions/33198597/… : (NSError * _Nullable * _Nullable) error
Elise van Looij
33

Según el Blog de Apple ("Nulabilidad y Objective-C") , puede utilizar

NS_ASSUME_NONNULL_BEGINy NS_ASSUME_NONNULL_END.

Dentro de estas regiones, se supondrá que cualquier tipo de puntero simple lo es nonnull. Luego puede agregar nullableun objeto que acepta valores NULL, que como

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
  • si el error es de NSError **tipo, debería serNSError * _Nullable * _Nullable
  • si el objeto es de id *tipo, mejor uso id _Nullable * _Nonnull, depende (puede ser que desee un _Nullable id * _Nullabletipo).
  • si el objeto es de NSObject *tipo, necesita poner anotación tras puntero, asíNSObject * _Nullable * _Nonnull

Nota

_Nonnully _Nullabledebe usarse después del puntero o id(Apple lo hace en el código de ejemplo AAPLListItem * _Nullable), pero las formas no subrayadas nonnully nullablepueden usarse después de un paréntesis abierto.

Sin embargo, en el caso común, hay una forma mucho más agradable de escribir estas anotaciones: dentro de las declaraciones de métodos, puede usar los formularios no subrayados nullablee nonnullinmediatamente después de un paréntesis abierto, siempre que el tipo sea un objeto simple o un puntero de bloque.

ver más en "Nulabilidad y Objective-C"

Por seguridad, hay algunas excepciones a esta regla:

  • typedeflos tipos no suelen tener una nulabilidad inherente; pueden ser fácilmente anulables o no anulables según el contexto. Por lo tanto, typedefno se supone que los tipos lo sean nonnull, incluso dentro de las regiones auditadas.
  • Los tipos de punteros más complejos como id *deben anotarse explícitamente. Por ejemplo, para especificar un puntero que no acepta valores NULL a una referencia de objeto que admite valores NULL, utilice _Nullable id * _Nonnull.
  • El tipo particular NSError **se usa tan a menudo para devolver errores a través de parámetros de método que siempre se asume que es un puntero anulable a una NSErrorreferencia anulable .

Se _Nullable id * _Nonnullpuede confundir, id _Nullable * _Nonnulles mejor comprensión.

_Nonnully _Nullabledebe usarse después del puntero o id(Apple lo hace en el código de ejemploAAPLListItem * _Nullable )

likid1412
fuente
Para propiedad débil, se aplicó _Nullable.
Dongjin Suh
3

También puedes hacer esto:

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

Solo depende de la sintaxis que más te guste.

OlDor
fuente
2

Para definir finalizaciones en un archivo de encabezado hice esto

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

Por supuesto, estoy de acuerdo con la respuesta aceptada.

Krishna Kumar
fuente
0

Del blog de desarrolladores de Apple : The Core: _Nullable y _Nonnull

puede utilizar los formularios no subrayados que aceptan nulos y no nulos inmediatamente después de un paréntesis abierto , siempre que el tipo sea un objeto simple o un puntero de bloque.

Los formularios no subrayados son más agradables que los subrayados, pero aún así necesitarás aplicarlos a todos los tipos de tu encabezado .

Parag Bafna
fuente
Sí, pero los que no son de subrayado (más agradables) no funcionan en declaraciones de bloque
Paul Bruneau
-2

Esto es lo que he usado para el caso NSError **:

-(BOOL) something:(int)number withError:(NSError *__autoreleasing  __nullable * __nullable)error;
Kevin
fuente
1
Como dijo Apple , NSError **no es necesario especificar su nulabilidad.
DawnSong