objective-c nullable objective-c-nullability

objective c - Diferencia entre nullable,__nullable y_Nullable en Objective-C



objective-c-nullability (4)

Con Xcode 6.3, se introdujeron nuevas anotaciones para expresar mejor la intención de las API en Objective-C (y para garantizar un mejor soporte de Swift, por supuesto). Esas anotaciones eran, por supuesto, no nonnull , null_unspecified y null_unspecified .

Pero con Xcode 7, aparecen muchas advertencias como:

Al puntero le falta un especificador de tipo de nulabilidad (_Nnnull, _Nullable o _Null_unspecified).

Además de eso, Apple usa otro tipo de especificadores de nulabilidad, marcando su código C ( source ):

CFArrayRef __nonnull CFArrayCreate( CFAllocatorRef __nullable allocator, const void * __nonnull * __nullable values, CFIndex numValues, const CFArrayCallBacks * __nullable callBacks);

En resumen, ahora tenemos estas 3 anotaciones de nulabilidad diferentes:

  • nonnull , nullable , null_unspecified
  • _Nonnull , _Nullable , _Null_unspecified
  • __nonnull , __nullable , __null_unspecified

Aunque sé por qué y dónde usar qué anotación, me confundo un poco qué tipo de anotaciones debo usar, dónde y por qué. Esto es lo que podría reunir:

  • Para las propiedades, debería usar nonnull , null_unspecified , null_unspecified .
  • Para los parámetros del método, debería usar nonnull , null_unspecified , null_unspecified .
  • Para los métodos C, debería usar __nonnull , __nullable , __null_unspecified .
  • Para otros casos, como los punteros dobles, debería usar _Nonnull , _Nullable , _Null_unspecified .

Pero todavía estoy confundido sobre por qué tenemos tantas anotaciones que básicamente hacen lo mismo.

Entonces mi pregunta es:

¿Cuál es la diferencia exacta entre esas anotaciones, cómo colocarlas correctamente y por qué?


De la documentation clang :

Los calificadores de nulabilidad (tipo) expresan si un valor de un tipo de puntero dado puede ser nulo (el calificador _Nullable ), no tiene un significado definido para nulo (el calificador _Nonnull ), o si el propósito de nulo no está claro (el _Null_unspecified Debido a que los calificadores de nulabilidad se expresan dentro del sistema de tipos, son más generales que los nonnull y returns_nonnull , lo que permite expresar (por ejemplo) un puntero anulable a una matriz de punteros no nulos. Los calificadores de nulabilidad se escriben a la derecha del puntero al que se aplican.

y

En Objective-C, hay una ortografía alternativa para los calificadores de nulabilidad que se pueden usar en métodos y propiedades de Objective-C utilizando palabras clave sensibles al contexto y sin subrayar

Por lo tanto, para las devoluciones de métodos y los parámetros, puede usar las versiones con doble guión __nonnull / __nullable / __null_unspecified lugar de las guiones con guiones bajos o con guiones bajos. La diferencia es que los subrayados simples y dobles deben colocarse después de la definición del tipo, mientras que los que no están subrayados deben colocarse antes de la definición del tipo.

Por lo tanto, las siguientes declaraciones son equivalentes y son correctas:

- (nullable NSNumber *)result - (NSNumber * __nullable)result - (NSNumber * _Nullable)result

Para parámetros:

- (void)doSomethingWithString:(nullable NSString *)str - (void)doSomethingWithString:(NSString * _Nullable)str - (void)doSomethingWithString:(NSString * __nullable)str

Para propiedades:

@property(nullable) NSNumber *status @property NSNumber *__nullable status @property NSNumber * _Nullable status

Sin embargo, las cosas se complican cuando se involucran dobles punteros o bloques que devuelven algo diferente a vacío, ya que los que no están subrayados no están permitidos aquí:

- (void)compute:(NSError * _Nullable * _Nullable)error - (void)compute:(NSError * __nullable * _Null_unspecified)error; // and all other combinations

Similar a los métodos que aceptan bloques como parámetros, tenga en cuenta que el calificador no nonnull / nullable se aplica al bloque y no a su tipo de retorno, por lo tanto, los siguientes son equivalentes:

- (void)executeWithCompletion:(nullable void (^)())handler - (void)executeWithCompletion:(void (^ _Nullable)())handler - (void)executeWithCompletion:(void (^ __nullable)())handler

Si el bloque tiene un valor de retorno, entonces se te obliga a una de las versiones de subrayado:

- (void)convertObject:(nullable id __nonnull (^)(nullable id obj))handler - (void)convertObject:(id __nonnull (^ _Nullable)())handler - (void)convertObject:(id _Nonnull (^ __nullable)())handler // the method accepts a nullable block that returns a nonnull value // there are some more combinations here, you get the idea

Como conclusión, puede usar cualquiera de los dos, siempre que el compilador pueda determinar el elemento al que asignar el calificador.


Del blog de Swift :

Esta característica se lanzó por primera vez en Xcode 6.3 con las palabras clave __nullable y __nonnull. Debido a posibles conflictos con bibliotecas de terceros, las hemos cambiado en Xcode 7 a _Nullable y _Nnnull que ves aquí. Sin embargo, para la compatibilidad con Xcode 6.3, hemos predefinido las macros __nullable y __nonnull para expandir a los nuevos nombres.


Muy útil es

NS_ASSUME_NONNULL_BEGIN

y cerrando con

NS_ASSUME_NONNULL_END

Esto anulará la necesidad del nivel de código ''nullibis'' :-), ya que tiene sentido suponer que todo no es nulo (o no nulo o nonnull o _nonnull __nonnull ) a menos que se indique lo contrario.

Lamentablemente, hay excepciones a esto también ...

  • no se asume que typedef s es __nonnull (nota, no nonnull no parece funcionar, tiene que usar su medio hermano feo)
  • id * necesita un nullibi explícito pero wow the sin-tax ( _Nullable id * _Nonnull <- adivina lo que eso significa ...)
  • NSError ** siempre se supone nulable

Entonces, con las excepciones a las excepciones y las palabras clave inconsistentes que provocan la misma funcionalidad, ¿quizás el enfoque sea usar las versiones feas __nonnull / __nullable / __null_unspecified e intercambiar cuando el cumplidor se queja ...? ¿Quizás es por eso que existen en los encabezados de Apple?

Curiosamente, algo lo puso en mi código ... Aborrezco los subrayados en el código (tipo de la vieja escuela Apple C ++), así que estoy absolutamente seguro de que no escribí estos pero aparecieron (un ejemplo de varios):

typedef void ( ^ DidReceiveChallengeBlock ) ( NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * __nullable credential );

Y aún más interesante, donde insertó el __nullable está mal ... (¡eek @!)

Realmente desearía poder usar la versión sin subrayado, pero aparentemente eso no vuela con el compilador ya que esto se marca como un error:

typedef void ( ^ DidReceiveChallengeBlock ) ( NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * nonnull credential );


Realmente me gustó este artículo , así que simplemente estoy mostrando lo que escribió el autor: https://swiftunboxed.com/interop/objc-nullability-annotations/

  • null_unspecified: puentes a un Swift opcional envuelto implícitamente. Este es el valor predeterminado .
  • nonnull : el valor no será nulo; puentes a una referencia regular.
  • nullable : el valor puede ser nulo; puentes a un opcional.
  • null_resettable : el valor nunca puede ser nulo cuando se lee, pero puede establecerlo en nulo para restablecerlo. Solo se aplica a propiedades.

Las anotaciones anteriores, luego difieren si las usa en el contexto de propiedades o funciones / variables:

El autor del artículo también proporcionó un buen ejemplo:

// property style @property (nonatomic, strong, null_resettable) NSString *name; // pointer style + (NSArray<NSView *> * _Nullable)interestingObjectsForKey:(NSString * _Nonnull)key; // these two are equivalent! @property (nonatomic, strong, nullable) NSString *identifier1; @property (nonatomic, strong) NSString * _Nullable identifier2;